home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / inventor / SpaceballViewer / SoSceneViewer.c++ < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  139.1 KB  |  4,654 lines

  1. /*
  2.  * Copyright (c) 1990-94 Silicon Graphics, Inc.
  3.  *
  4.  * Permission to use, copy, modify, distribute, and sell this software and
  5.  * its documentation for any purpose is hereby granted without fee, provided
  6.  * that the name of Silicon Graphics may not be used in any advertising or
  7.  * publicity relating to the software without the specific, prior written
  8.  * permission of Silicon Graphics.
  9.  *
  10.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
  11.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
  12.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
  13.  *
  14.  * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
  15.  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
  16.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE
  17.  * POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
  18.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  19.  */
  20. /*
  21.  * Copyright (C) 1990-93   Silicon Graphics, Inc.
  22.  *
  23.  * 
  24.  _______________________________________________________________________
  25.  ______________  S I L I C O N   G R A P H I C S   I N C .  ____________
  26.  |
  27.  |   $Revision: 1.1064 $
  28.  |
  29.  |   Classes    : SoSceneViewer
  30.  |
  31.  |   Author(s)    : Thad Beier, David Mott, Alain Dumesny, Paul Isaacs,
  32.  |          Rikk Carey, Dave Immel
  33.  |
  34.  ______________  S I L I C O N   G R A P H I C S   I N C .  ____________
  35.  _______________________________________________________________________
  36.  */
  37.  
  38. // Define this to have menus appear in the popup planes
  39. // instead of the normal planes. You lose menu colors, 
  40. // but don't have to redraw the scene just to see a menu.
  41. #define MENUS_IN_POPUP
  42.  
  43. #include <stdlib.h> // for system() and getenv()
  44. #include <unistd.h> // for access()
  45.  
  46. #include <X11/StringDefs.h>
  47. #include <X11/Intrinsic.h>
  48. #include <X11/Xatom.h>
  49.  
  50. #include <Xm/Xm.h>
  51. #include <Xm/BulletinB.h>
  52. #include <Xm/CascadeB.h>
  53. #include <Xm/CascadeBG.h>
  54. #include <Xm/FileSB.h>
  55. #include <Xm/Form.h>
  56. #include <Xm/Label.h>
  57. #include <Xm/FileSB.h>
  58. #include <Xm/PushB.h>
  59. #include <Xm/PushBG.h>
  60. #include <Xm/SeparatoG.h>
  61. #include <Xm/Text.h>
  62. #include <Xm/ToggleB.h>
  63. #include <Xm/ToggleBG.h>
  64. #include <Xm/DialogS.h>
  65. #include <Xm/LabelG.h>
  66.  
  67.  
  68. #include <Inventor/So.h>
  69. #include <Inventor/SoDB.h>
  70. #include <Inventor/SoNodeKitPath.h>
  71. #include <Inventor/SoPickedPoint.h>
  72. #include <Inventor/Xt/SoXt.h>
  73. #include <Inventor/Xt/SoXtClipboard.h>
  74. #include <Inventor/Xt/SoXtDirectionalLightEditor.h>
  75. #include <Inventor/Xt/SoXtMaterialEditor.h>
  76. #include <Inventor/Xt/SoXtPrintDialog.h>
  77. #include <Inventor/Xt/SoXtResource.h>
  78. #include <Inventor/Xt/SoXtTransformSliderSet.h>
  79. #include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
  80. #include <Inventor/Xt/viewers/SoXtFlyViewer.h>
  81. #include <Inventor/Xt/viewers/SoXtPlaneViewer.h>
  82. #include <Inventor/Xt/viewers/SoXtWalkViewer.h>
  83. #include <Inventor/Xt/devices/SoXtSpaceball.h>
  84. #include <Inventor/actions/SoBoxHighlightRenderAction.h>
  85. #include <Inventor/actions/SoGetBoundingBoxAction.h>
  86. #include <Inventor/actions/SoGetMatrixAction.h>
  87. #include <Inventor/actions/SoSearchAction.h>
  88. #include <Inventor/actions/SoWriteAction.h>
  89. #include <Inventor/details/SoNodeKitDetail.h>
  90. #include <Inventor/draggers/SoDirectionalLightDragger.h>
  91. #include <Inventor/draggers/SoTabBoxDragger.h>
  92. #include <Inventor/manips/SoCenterballManip.h>
  93. #include <Inventor/manips/SoDirectionalLightManip.h>
  94. #include <Inventor/manips/SoHandleBoxManip.h>
  95. #include <Inventor/manips/SoJackManip.h>
  96. #include <Inventor/manips/SoPointLightManip.h>
  97. #include <Inventor/manips/SoSpotLightManip.h>
  98. #include <Inventor/manips/SoTabBoxManip.h>
  99. #include <Inventor/manips/SoTrackballManip.h>
  100. #include <Inventor/manips/SoTransformBoxManip.h>
  101. #include <Inventor/nodekits/SoBaseKit.h>
  102. #include <Inventor/nodes/SoCallback.h>
  103. #include <Inventor/nodes/SoCube.h>
  104. #include <Inventor/nodes/SoDirectionalLight.h>
  105. #include <Inventor/nodes/SoEnvironment.h>
  106. #include <Inventor/nodes/SoLabel.h>
  107. #include <Inventor/nodes/SoLight.h>
  108. #include <Inventor/nodes/SoMaterial.h>
  109. #include <Inventor/nodes/SoPointLight.h>
  110. #include <Inventor/nodes/SoSelection.h>
  111. #include <Inventor/nodes/SoShape.h>
  112. #include <Inventor/nodes/SoSpotLight.h>
  113. #include <Inventor/nodes/SoSwitch.h>
  114. #include <Inventor/nodes/SoTranslation.h>
  115. #include <Inventor/nodes/SoTexture2.h>
  116. #include <Inventor/events/SoEvents.h>
  117.  
  118. #include "SoSceneViewer.h"
  119. #include "SoSceneMenu.h"
  120. #include "SvManipList.h"
  121. #include "MyColorEditor.h"
  122. #include "InventorLogo.h"
  123. #include "MyTextureEditor.h"
  124. #include "MySlider.h"
  125.  
  126. #include <GL/gl.h>
  127. #include <malloc.h>
  128. #ifdef DEBUG
  129. #include <assert.h>
  130. #endif
  131.  
  132. //
  133. //  Macros and constants
  134. //
  135.  
  136. // toggle button macros
  137. #define TOGGLE_ON(BUTTON) \
  138.     XmToggleButtonSetState((Widget) BUTTON, TRUE, FALSE)
  139. #define TOGGLE_OFF(BUTTON) \
  140.     XmToggleButtonSetState((Widget) BUTTON, FALSE, FALSE)
  141.  
  142. #define    FOG_FUDGE    1.6
  143. #define SV_NUM_LIGHTS    6
  144.  
  145. #define SWITCH_LIGHT_OFF(SWITCH) (SWITCH)->whichChild.setValue(SO_SWITCH_NONE)
  146. #define SWITCH_LIGHT_ON(SWITCH)  (SWITCH)->whichChild.setValue(SO_SWITCH_ALL)
  147. #define IS_LIGHT_ON(SWITCH)     ((SWITCH)->whichChild.getValue() == SO_SWITCH_ALL)
  148.  
  149. #define SV_ENV_LABEL "SoSceneViewer Environment v3.0"
  150.  
  151. //
  152. //  Structs
  153. //
  154.  
  155. struct SoSceneViewerData {
  156.     int id;
  157.     SoSceneViewer *classPt;
  158.     Widget widget;
  159. };
  160.  
  161. class SvLightData {
  162.   public:
  163.     // Constructor inits everything to NULL
  164.     SvLightData();
  165.     
  166.     SoSceneViewer   *classPt;
  167.     SoSwitch        *lightSwitch;
  168.     SoTranslation   *translation;   // for placing a directional light manip
  169.     SoScale         *scale;
  170.     SoLight        *light;
  171.     SoScale         *scaleInverse;
  172.     SoTranslation   *translationInverse;
  173.     SoType        type;
  174.     char        *name;
  175.     MyColorEditor *colorEditor;
  176.     SbBool             isManip;
  177.     SbBool             shouldBeManip; // Used to remember what it was when
  178.                    // they all get turned off for writing,
  179.                    // printing, etc.
  180.     Widget        cascadeWidget;
  181.     Widget        submenuWidget;
  182.     Widget        onOffWidget;
  183.     Widget        iconWidget;
  184.     Widget        editColorWidget;
  185.     Widget        removeWidget;
  186. };
  187.  
  188. SvLightData::SvLightData() 
  189. {
  190.     classPt = NULL;
  191.     lightSwitch = NULL;
  192.     translation = NULL;
  193.     scale = NULL;
  194.     light = NULL;
  195.     scaleInverse = NULL;
  196.     translationInverse = NULL;
  197.     name = NULL;
  198.     colorEditor = NULL;
  199.     cascadeWidget = NULL;
  200.     submenuWidget = NULL;
  201.     onOffWidget = NULL;
  202.     iconWidget = NULL;
  203.     editColorWidget = NULL;
  204.     removeWidget = NULL;
  205. }
  206.  
  207. ////////////////////////////////////////////////////////////////////////
  208. //
  209. //  Set the Inventor logo on the screen.
  210. //
  211. static void
  212. logoCB(void *, SoAction *action)
  213. {
  214.     if (action->isOfType(SoGLRenderAction::getClassTypeId())) {
  215.     glViewport(0, 0, 80, 80);
  216.     }
  217. }
  218.  
  219. static void
  220. setOverlayLogo(SoXtRenderArea *ra)
  221. {
  222.     static SoSeparator *logo = NULL;
  223.     
  224.     if (logo == NULL) {
  225.     SoInput in;
  226.     in.setBuffer((void *)ivLogo, ivLogoSize);
  227.     logo = SoDB::readAll(&in);
  228.     logo->ref();
  229.     
  230.     // Add a callback node which will set the viewport
  231.     SoCallback *cb = new SoCallback;
  232.     cb->setCallback(logoCB);
  233.     logo->insertChild(cb, 0);
  234.     }
  235.     
  236.     SbColor col(1, 1, 1);
  237.     ra->setOverlayColorMap(1, 1, &col);
  238.     ra->setOverlaySceneGraph(logo);    
  239. }
  240. //
  241. ////////////////////////////////////////////////////////////////////////
  242.  
  243. ////////////////////////////////////////////////////////////////////////
  244. //
  245. // Public constructor - build the widget right now
  246. //
  247. SoSceneViewer::SoSceneViewer(
  248.     Widget parent,
  249.     const char *name, 
  250.     SbBool buildInsideParent, 
  251.     SoSelection *inputGraph,
  252.     const char *envFile) 
  253.     : SoXtComponent(
  254.         parent,
  255.         name, 
  256.         buildInsideParent) 
  257. //
  258. ////////////////////////////////////////////////////////////////////////
  259. {
  260.     // In this case, render area is what the app wants, so buildNow = TRUE
  261.     constructorCommon(inputGraph, envFile, TRUE);
  262. }
  263.  
  264. ////////////////////////////////////////////////////////////////////////
  265. //
  266. // SoEXTENDER constructor - the subclass tells us whether to build or not
  267. //
  268. SoSceneViewer::SoSceneViewer(
  269.     Widget parent,
  270.     const char *name, 
  271.     SbBool buildInsideParent, 
  272.     SoSelection *inputGraph,
  273.     const char *envFile, 
  274.     SbBool buildNow)
  275.     : SoXtComponent(
  276.         parent,
  277.         name, 
  278.         buildInsideParent) 
  279. //
  280. ////////////////////////////////////////////////////////////////////////
  281. {
  282.     // In this case, render area may be what the app wants, 
  283.     // or it may want a subclass of render area. Pass along buildNow
  284.     // as it was passed to us.
  285.     constructorCommon(inputGraph, envFile, buildNow);
  286. }
  287.  
  288. ////////////////////////////////////////////////////////////////////////
  289. //
  290. // Called by the constructors
  291. //
  292. // private
  293. //
  294. void
  295. SoSceneViewer::constructorCommon(
  296.     SoSelection *inputGraph,
  297.     const char *envFile, 
  298.     SbBool buildNow)
  299. //
  300. ////////////////////////////////////////////////////////////////////////
  301. {
  302.     Arg        args[1];
  303.     int        i;
  304.  
  305.     
  306.     setClassName("SoSceneViewer");
  307.     setSize( SbVec2s(520, 510) );
  308.  
  309.     // selection is the users scene graph.
  310.     selection  = inputGraph;
  311.     currentViewer = NULL;
  312.     spaceball = NULL;
  313.     //  SPACEBALL
  314.     //  Check for the scenegraph generated by dxfToIv.  If its there,
  315.     //  clip it out of the scenegraph.  See comments in fixDXFSceneGraph
  316.     //  for more information.
  317.     if (selection->getNumChildren() != 0) {
  318.         fixDXFSceneGraph((SoGroup *) inputGraph->getChild(0));
  319.     }
  320.     createLightsCameraEnvironment();
  321.     
  322.     // the scene viewer supplies its own camera and lights.
  323.     // in fact, we remove any cameras that might be in the users graph.
  324.     // NOTE: since the camera may be switched by the viewer (ortho/perspective toggle)
  325.     // make sure to get the camera from the viewer (and not cache the camera).
  326.     sceneGraph = new SoSeparator();
  327.     sceneGraph->ref();    // must ref it
  328.     sceneGraph->addChild(lightsCameraEnvironment);
  329.     sceneGraph->addChild(selection);
  330. #ifndef EXPLORER
  331.         removeCameras(selection);
  332. #endif
  333.     
  334.     //
  335.     // Widget and menu variables
  336.     //
  337.     mgrWidget = NULL;
  338.     showMenuFlag = TRUE;
  339.     menuWidget = NULL;
  340.     menuItems = new SoSceneViewerData[SV_MENU_NUM];
  341.     for (i=0; i<SV_MENU_NUM; i++) {
  342.     menuItems[i].id = i;
  343.     menuItems[i].classPt = this;
  344.     menuItems[i].widget = NULL;
  345.     }
  346.     popupWidget = NULL;
  347.     
  348.     //
  349.     // File
  350.     //
  351.     fileName     = NULL;
  352.     fileDialog     = NULL;
  353.     printDialog     = NULL;
  354.     
  355.     //
  356.     // Viewing
  357.     //
  358.     // Allocate only one viewer at a time. The other viewers will
  359.     // be allocated as needed to increase speed and save memory.
  360.     //
  361.     for (i=0; i<4; i++)
  362.     viewerList[i] = NULL;
  363.     
  364.     
  365.     // fog
  366.     fogFlag = FALSE;
  367.     environment->fogType.setValue( SoEnvironment::NONE );    
  368.  
  369.     antialiasingFlag = FALSE;
  370.     backgroundColorEditor = NULL;
  371.  
  372.     //
  373.     // Selection
  374.     //     These callbacks are used to update the SceneViewer state after
  375.     //     the current selection changes (e.g. attach/detach editors and manips).
  376.     //
  377.     selection->addSelectionCallback(SoSceneViewer::selectionCallback, this);
  378.     selection->addDeselectionCallback(SoSceneViewer::deselectionCallback, this);
  379.     selection->addDeselectionCallback(SoSceneViewer::deselectionCallback, this);
  380.     selection->setPickFilterCallback(SoSceneViewer::pickFilterCB, this);
  381.  
  382.     highlightRA = new SoBoxHighlightRenderAction;
  383.     
  384.     //
  385.     // Editors
  386.     //
  387.     ignoreCallback = FALSE;
  388.     materialEditor  = NULL;
  389.     colorEditor        = NULL;
  390.     transformSliderSet = NULL;
  391.  
  392.     //
  393.     // Manips
  394.     //
  395.     curManip = SV_NONE;
  396.     highlightRA->setVisible(TRUE); // highlight visible when no manip
  397.     curManipReplaces = TRUE;
  398.     maniplist = new SvManipList;
  399.     
  400. #ifdef EXPLORER
  401.     //
  402.     // User callback
  403.     //
  404.     userModeCB = NULL;
  405.     userModedata = NULL;
  406.     userModeFlag = FALSE;
  407. #endif /* EXPLORER */
  408.     
  409.     //
  410.     // Lights
  411.     //
  412.     ambientColorEditor = NULL;
  413.     headlightData = new SvLightData;
  414.     headlightData->classPt = this;
  415.     headlightData->name = strdup("Headlight");
  416.     headlightData->type = SoDirectionalLight::getClassTypeId();
  417.     headlightData->colorEditor = NULL;
  418.     headlightData->isManip = FALSE;
  419.     headlightData->shouldBeManip = FALSE;
  420.     headlightEditor = NULL;
  421.     calculatedLightManipSize = FALSE;
  422.     
  423.     // Build the widget tree, and let SoXtComponent know about our base widget.
  424.     if (buildNow) {
  425.     Widget w = buildWidget(getParentWidget());
  426.     setBaseWidget(w);
  427.     }
  428.     
  429.     // do this after everything else has been set up (viewer needs to exists 
  430.     // because the camera is gotten from the viewer (not cached here))
  431.     if (envFile != NULL)
  432.         readEnvFile(envFile);
  433. }
  434.  
  435. ////////////////////////////////////////////////////////////////////////
  436. //
  437. // Description:
  438. //  This is a hack that removes some wierd light stuff that is put
  439. //  at the top of sceneGraphs by the dxftoiv translator.
  440. //  THe pattern i'm looking for is:
  441. //       Group {
  442. //          Separator {
  443. //              Group {
  444. //                 Rotation {
  445. //                 }
  446. //                 DirectionalLight {
  447. //                 }
  448. //                 ResetTransform {
  449. //                 }
  450. //  If i find it, i prune the 2nd Group and all its children from the
  451. //  tree.  This allows me to put a transfrom at the top of the scenegraph
  452. //  and actually have that transform effect the entire scene as opposed to
  453. //  the directional light only.
  454. /////////////////////////////////////////////////////////////////////////
  455.  
  456.  
  457. void
  458. SoSceneViewer::fixDXFSceneGraph(SoGroup *sceneRoot) {
  459.    SoNode    *node;
  460.    SoSeparator    *sep;
  461.    if (sceneRoot == NULL) {
  462.       return;
  463.    }
  464.    if (sceneRoot->isOfType(SoGroup::getClassTypeId()) == TRUE) {
  465.       node = ((SoGroup *) (sceneRoot))->getChild(0);
  466.    } else {
  467.       return;
  468.    }
  469.    if (node->isOfType(SoSeparator::getClassTypeId()) == TRUE) {
  470.       sep = (SoSeparator *) node;
  471.       node = ((SoGroup *) (sep))->getChild(0);
  472.    } else {
  473.       return;
  474.    }
  475.    if (node->isOfType(SoGroup::getClassTypeId()) == TRUE) {
  476.       SoGroup *group = (SoGroup *) node;
  477.       if (group->getNumChildren() == 3) {
  478.          if (group->getChild(0)->isOfType(SoRotation::getClassTypeId()) &&
  479.             group->getChild(1)->isOfType(SoDirectionalLight::getClassTypeId()) 
  480.                &&
  481.             group->getChild(2)->isOfType(SoResetTransform::getClassTypeId())) {
  482.             sep->removeChild(group);
  483.          } else {
  484.             return;
  485.          }
  486.       } else {
  487.          return;
  488.       }
  489.    }
  490. }
  491.  
  492. ////////////////////////////////////////////////////////////////////////
  493. //
  494. // Description:
  495. //    Destructor.
  496. //
  497. // Use: public
  498.  
  499. SoSceneViewer::~SoSceneViewer()
  500. //
  501. ////////////////////////////////////////////////////////////////////////
  502. {
  503.     // detach and delete the manips
  504.     detachManipFromAll();
  505.     delete maniplist;
  506.  
  507.     // detach and delete the viewers
  508.     currentViewer->setSceneGraph(NULL);
  509.     delete (SoXtExaminerViewer *) viewerList[SV_VWR_EXAMINER];
  510.     delete (SoXtFlyViewer *) viewerList[SV_VWR_FLY];
  511.     delete (SoXtWalkViewer *) viewerList[SV_VWR_WALK];
  512.     delete (SoXtPlaneViewer *) viewerList[SV_VWR_PLANE];
  513.     
  514.     // delete menu items data
  515.     delete [ /*SV_MENU_NUM*/ ] menuItems;
  516.     delete headlightData;
  517.     delete headlightEditor;
  518.     
  519.     delete printDialog;
  520.     
  521.     // Editor components
  522.     delete materialEditor;
  523.     delete colorEditor;
  524.     delete transformSliderSet;
  525.     delete ambientColorEditor;
  526.     delete backgroundColorEditor;
  527.  
  528.     sceneGraph->unref();
  529. }
  530.  
  531. ////////////////////////////////////////////////////////////////////////
  532. //
  533. // Description:
  534. //    New data is going to be coming into the viewer.  Time to disconnect all
  535. //  manipulators and picking, and wait for new information.  Might as well go
  536. //  into a viewing mode as well, this gets rid of the manipulators, and puts
  537. //  the user in control of viewing when new data shows up.
  538. //
  539. // Use: public
  540. void
  541. SoSceneViewer::newData()
  542. //
  543. ////////////////////////////////////////////////////////////////////////
  544. {
  545.     selection->deselectAll();
  546. }
  547.  
  548. #ifdef EXPLORER
  549. ////////////////////////////////////////////////////////////////////////
  550. //
  551. // Description:
  552. //    This sets the user mode callack routine
  553. //
  554. // Use: public
  555. void 
  556. SoSceneViewer::setUserModeEventCallback(SoXtRenderAreaEventCB *fcn)
  557. //
  558. ////////////////////////////////////////////////////////////////////////
  559. {
  560.     userModeCB = fcn;
  561.     userModedata = currentViewer->getSceneGraph();
  562.     if (userModeFlag)
  563.     currentViewer->setEventCallback(userModeCB, userModedata);
  564. }
  565. #endif /* EXPLORER */
  566.  
  567. #ifdef notdef
  568. ////////////////////////////////////////////////////////////////////////
  569. //
  570. // Description:
  571. //    switches from the current viewer to the given viewer. The new
  572. //  viewer will automatically be allocated if needed, and set to have 
  573. //  the same settings the current viewer has (drawing style, buffer 
  574. //  type, etc..).
  575. //
  576. // Use: private
  577. void
  578. SoSceneViewer::switchToViewer(SvEViewer newViewer)
  579. //
  580. ////////////////////////////////////////////////////////////////////////
  581. {
  582.     if (whichViewer == newViewer)
  583.         return;
  584.     
  585.     // remove the sensor + scene graph from the old viewer (to prevent
  586.     // an unecessary redraw of the old viewer)
  587.     currentViewer->setAutoRedraw(FALSE);
  588.     currentViewer->setSceneGraph(NULL);
  589.     
  590.     // allocate the viewer if needed and set the window title.
  591.     // all viewers share the same highlight render action.
  592.     switch (newViewer) {
  593.     case SV_VWR_EXAMINER:
  594.         // examiner vwr is already created in build()
  595.         setTitle("SceneViewer (Examiner)");
  596.         break;
  597.     case SV_VWR_FLY:
  598.         if ( viewerList[newViewer] == NULL ) {
  599.         viewerList[newViewer] = new SoXtFlyViewer(mgrWidget);
  600.         viewerList[newViewer]->setGLRenderAction(highlightRA);
  601.         viewerList[newViewer]->redrawOnSelectionChange(selection);
  602.         }
  603.         setTitle("SceneViewer (Fly)");
  604.         break;
  605.     case SV_VWR_WALK:
  606.         if ( viewerList[newViewer] == NULL ) {
  607.         viewerList[newViewer] = new SoXtWalkViewer(mgrWidget);
  608.         viewerList[newViewer]->setGLRenderAction(highlightRA);
  609.         viewerList[newViewer]->redrawOnSelectionChange(selection);
  610.         }
  611.         setTitle("SceneViewer (Walk)");
  612.         break;
  613.     case SV_VWR_PLANE:
  614.         if ( viewerList[newViewer] == NULL ) {
  615.         viewerList[newViewer] = new SoXtPlaneViewer(mgrWidget);
  616.         viewerList[newViewer]->setGLRenderAction(highlightRA);
  617.         viewerList[newViewer]->redrawOnSelectionChange(selection);
  618.         }
  619.         setTitle("SceneViewer (Plane)");
  620.         break;
  621.     }
  622.     SoXtFullViewer *newVwr = viewerList[newViewer];
  623.     
  624.     // re-init the render action (since it is shared between viewers) now that
  625.     // we are changing windows + set the new scene graph
  626.     newVwr->setAutoRedraw(TRUE);
  627.     newVwr->setSceneGraph(sceneGraph);
  628.     newVwr->getGLRenderAction()->invalidateState();
  629.     
  630.     //
  631.     // make sure the new viewer has all the same settings as 
  632.     // the current viewer. 
  633.     //
  634.     
  635.     // XtRenderArea methods
  636.     //???can't each viewer have it's own background color? especially
  637.     //???if we nuke the background color editor. The user might set the
  638.     //???colors to be different in the app-defaults file.
  639.     //???newVwr->setBackgroundColor( currentViewer->getBackgroundColor() );
  640.     
  641.     environment->fogColor.setValue( newVwr->getBackgroundColor() );
  642.     
  643.     newVwr->setClearBeforeRender( currentViewer->isClearBeforeRender() );
  644.     // XtViewer methods
  645.     newVwr->setHeadlight( currentViewer->isHeadlight() );
  646.     newVwr->setDrawStyle( 
  647.         SoXtViewer::STILL,
  648.         currentViewer->getDrawStyle(SoXtViewer::STILL) );
  649.     newVwr->setDrawStyle( 
  650.         SoXtViewer::INTERACTIVE,
  651.         currentViewer->getDrawStyle(SoXtViewer::INTERACTIVE) );
  652.     newVwr->setBufferingType( currentViewer->getBufferingType() );
  653.     newVwr->setViewing( currentViewer->isViewing() );
  654.     newVwr->setAutoClipping( currentViewer->isAutoClipping() );
  655.     newVwr->setSeekTime( currentViewer->getSeekTime() );
  656.     // XtFullViewer methods
  657.     newVwr->setDecoration( currentViewer->isDecoration() );
  658.  
  659.     // The tabBoxManip adds a finish callback to the viewer.
  660.     // We need to remove from the old and add to the new.
  661.     for (int m = 0; m < maniplist->getLength(); m++ ) {
  662.     SoTransformManip *manip = maniplist->getManip(m);    
  663.     if ( manip->isOfType( SoTabBoxManip::getClassTypeId() ) ) {
  664.  
  665.         currentViewer->removeFinishCallback( 
  666.         &SoSceneViewer::adjustScaleTabSizeCB, manip->getDragger() );
  667.         newVwr->addFinishCallback( 
  668.             &SoSceneViewer::adjustScaleTabSizeCB, manip->getDragger() );
  669.     }
  670.     }
  671.     
  672. #ifdef EXPLORER
  673.     if (userModeFlag)
  674.     newVwr->setEventCallback(userModeCB, userModedata);
  675.     else
  676.     newVwr->setEventCallback(NULL, NULL);
  677. #endif
  678.     
  679.     // build and layout the new viewer
  680.     buildAndLayoutViewer(newVwr);
  681.     
  682.     // finally switch to the new viewer by showing the new viewer, 
  683.     // and hidding the old viewer (hide is done last to reduce flicker).
  684.     newVwr->show();
  685.     currentViewer->hide();
  686.     whichViewer = newViewer;
  687.     currentViewer = newVwr;
  688.     //setOverlayLogo(currentViewer);
  689. }
  690. #endif
  691.  
  692. ////////////////////////////////////////////////////////////////////////
  693. //
  694. // Description:
  695. //    Move up the picked path to the parent group.
  696. //
  697. // Use: public
  698.  
  699. void
  700. SoSceneViewer::pickParent()
  701. //
  702. ////////////////////////////////////////////////////////////////////////
  703. {
  704.     SoFullPath  *pickPath;
  705.     int            parentIndex;
  706.  
  707.     // We'll pick the parent of the last selection in the list...
  708.     pickPath = (SoFullPath *) (*selection)[selection->getNumSelected() - 1];    
  709.     if(pickPath == NULL || pickPath->getLength() < 2)
  710.     return;
  711.  
  712.     // Get actual node that is the current selection:
  713.     SoNode *tail     = pickPath->getTail();
  714.     SoNode *kitTail  = ((SoNodeKitPath *)pickPath)->getTail();
  715.     SoType nkt = SoBaseKit::getClassTypeId();
  716.     if ( kitTail->isOfType( nkt ) )
  717.     tail = kitTail;
  718.     else
  719.     kitTail = NULL;
  720.  
  721.     // If kitTail is at top of path, we've already gone as high as we can go.
  722.     if ( kitTail == pickPath->getHead() )
  723.     return;
  724.  
  725.     // Get index of parent of selection.
  726.     if ( kitTail != NULL ) {
  727.         // Look for first kit above tail. If none, use direct parent of kitTail.
  728.     SoNode *aboveTail = ((SoNodeKitPath *)pickPath)->getNodeFromTail( 1 );
  729.     SbBool aboveIsKit = aboveTail->isOfType( nkt );
  730.     for (int i = pickPath->getLength() - 1; i >= 0; i-- ) {
  731.         if ( aboveIsKit ) {
  732.         if (pickPath->getNode(i) == aboveTail ) {
  733.             parentIndex = i;
  734.             break;
  735.         }
  736.         }
  737.         else if ( pickPath->getNode(i) == kitTail ) {
  738.         parentIndex = i - 1;
  739.         break;
  740.         }
  741.     }
  742.     }
  743.     else {
  744.     // If tail is not a nodkeit, parentIndex is just parent of tail...
  745.     parentIndex = pickPath->getLength() - 2;
  746.     }
  747.     
  748.     // cannot select the selection node (make sure we're not)
  749.     if (pickPath->getNode(parentIndex) == selection) {
  750.         fprintf(stderr, "No more parents to pick (cannot pick above the selection node)\n");
  751.         return;
  752.     }
  753.     
  754.     pickPath->ref();                // need to ref it, because
  755.                         // selection->clear unref's it
  756.     selection->deselectAll();
  757.     pickPath->truncate(parentIndex + 1);        // Make path end at parentIndex
  758.     selection->select(pickPath);        // add path back in
  759.     pickPath->unref();                // now we can unref it, again
  760. }
  761.  
  762. ////////////////////////////////////////////////////////////////////////
  763. //
  764. // Description:
  765. //    Pick all group nodes and shapes under selection.
  766. //
  767. // Use: private
  768. void
  769. SoSceneViewer::pickAll()
  770. //
  771. ////////////////////////////////////////////////////////////////////////
  772. {
  773.     selection->deselectAll();
  774.  
  775. #ifdef DEBUG
  776.     assert(selection != NULL);
  777. #endif
  778.  
  779.     SoPathList myPaths;
  780.  
  781.     // Our callbacks on the selection's 'select' method may add 
  782.     // more children to the selection node (by making a trackball or handlebox)
  783.     // Therefore, we must first determine the selections by storing
  784.     // paths to them.
  785.     // Following this, we call 'select' on each path, in turn.
  786.  
  787.     //
  788.     // Create paths from the selection node to all of it's children
  789.     // that are groups or shapes.
  790.     //
  791.     for (int i = 0; i < selection->getNumChildren(); i++) {
  792.         SoNode *node = selection->getChild(i);
  793.     if ((node->isOfType(SoGroup::getClassTypeId()) ||
  794.          node->isOfType(SoShape::getClassTypeId())) )
  795.     {
  796.         SoPath *thisPath = new SoPath(selection);
  797.         thisPath->append(i);
  798.  
  799.         myPaths.append( thisPath );
  800.     }
  801.     }
  802.  
  803.     //
  804.     // Select each path in 'myPaths'
  805.     //
  806.     for (int j = 0; j < myPaths.getLength(); j++)
  807.     selection->select(myPaths[j]);
  808. }
  809.  
  810.  
  811. ////////////////////////////////////////////////////////////////////////
  812. //
  813. // Description:
  814. //    This routine first detaches manipulators from all selected objects,
  815. //      then attaches a manipulator to all selected objects.
  816. //
  817. // Use: private
  818.  
  819. void
  820. SoSceneViewer::replaceAllManips( 
  821.     SvEManipMode manipMode )        // Current manipulator
  822. //
  823. ////////////////////////////////////////////////////////////////////////
  824. {
  825.     detachManipFromAll();
  826.     attachManipToAll( manipMode );
  827. }
  828.  
  829. ////////////////////////////////////////////////////////////////////////
  830. //
  831. // Description:
  832. //    This routine attaches a manipulator to all selected objects.
  833. //
  834. // Use: private
  835.  
  836. void
  837. SoSceneViewer::attachManipToAll( 
  838.     SvEManipMode manipMode )        // Current manipulator
  839. //
  840. ////////////////////////////////////////////////////////////////////////
  841. {
  842.     int        i;
  843.  
  844.     for ( i = 0; i < selection->getNumSelected(); i++ ) {
  845.     SoPath *p = (*selection)[i];
  846.     attachManip( manipMode, p );
  847.     }
  848. }
  849.  
  850. ////////////////////////////////////////////////////////////////////////
  851. //
  852. // Description:
  853. //    This routine attaches and activates a manipulator.  
  854. //
  855. // Use: private
  856.  
  857. void
  858. SoSceneViewer::attachManip( 
  859.     SvEManipMode manipMode,        // Current manipulator
  860.     SoPath *selectedPath )    // Which selection to attach to
  861. //
  862. ////////////////////////////////////////////////////////////////////////
  863. {
  864.     SoTransformManip     *theXfManip;
  865.     SoPath        *xfPath;
  866.  
  867.     //
  868.     // Attach to a manipulator.
  869.     //
  870.  
  871.     if ( manipMode == SV_NONE )
  872.     return;
  873.  
  874.     xfPath = findTransformForAttach( selectedPath );
  875.     xfPath->ref();
  876.     theXfManip = NULL;
  877.  
  878.     switch( manipMode ) {
  879.       case SV_TRACKBALL:
  880.     theXfManip = new SoTrackballManip;
  881.     break;
  882.  
  883.       case SV_HANDLEBOX:
  884.     theXfManip = new SoHandleBoxManip;
  885.  
  886.       break;
  887.  
  888.       case SV_JACK:
  889.     theXfManip = new SoJackManip;
  890.       break;
  891.  
  892.       case SV_CENTERBALL:
  893.     theXfManip = new SoCenterballManip;
  894.       break;
  895.  
  896.       case SV_XFBOX:
  897.     theXfManip = new SoTransformBoxManip;
  898.       break;
  899.  
  900.       case SV_TABBOX:
  901.     theXfManip = new SoTabBoxManip;
  902.       break;
  903.  
  904.       case SV_NONE:
  905.     return;
  906.     }
  907.  
  908.     if ( theXfManip ) {
  909.  
  910.     SoFullPath *fp = (SoFullPath *) xfPath;
  911.  
  912. #ifdef DEBUG
  913.     if ( !fp->getTail()->isOfType( SoTransform::getClassTypeId() ) ) {
  914.         fprintf(stderr,"DBG> Fatal Error: in SoSceneViewer::attachManip\n");
  915.         fprintf(stderr,"   > end of path is not a transform\n");
  916.     }
  917. #endif
  918.     SoTransform *oldXf = (SoTransform *)fp->getTail();
  919.     oldXf->ref();
  920.     theXfManip->ref();
  921.  
  922.     if ( !theXfManip->replaceNode( xfPath ) ) {
  923.         theXfManip->unref();
  924. #ifdef DEBUG
  925.         fprintf(stderr,"DBG> Fatal Error: in SoSceneViewer::attachManip\n");
  926.         fprintf(stderr,"   > manip->replaceNode() failed!\n" );
  927. #endif
  928.     }
  929.  
  930.     // If the transformSliderSet is attached to the oldXf, then attach 
  931.     // it to the new manip instead.
  932.     if ( transformSliderSet && transformSliderSet->isVisible()
  933.          && transformSliderSet->getNode() == oldXf)
  934.         transformSliderSet->setNode(theXfManip);
  935.  
  936.     // Add manip and paths to the maniplist (maniplist will ref/unref)
  937.     maniplist->append(selectedPath, theXfManip, xfPath );
  938.  
  939.     theXfManip->unref();
  940.     oldXf->unref();
  941.  
  942.     if ( manipMode == SV_TABBOX ) {
  943.         // Special case!  When using a  tab box, we want to adjust the
  944.         // scale tabs upon viewer finish.
  945.         currentViewer->addFinishCallback( 
  946.            &SoSceneViewer::adjustScaleTabSizeCB,theXfManip->getDragger());
  947.     }
  948.     if ( manipMode == SV_JACK ) {
  949.         // Special case! For jack manip, we want it so that clicking on the
  950.         // selected object initiates 2-dimensional translation.  Other
  951.         // parts of the jack manip should use the default resource geometry.
  952.         // So, we replace the parts that do planar motion. 
  953.         // We need to replace a total of six parts:
  954.         //     'translator.yzTranslator.translator':
  955.         //     'translator.xzTranslator.translator': 
  956.         //     'translator.xyTranslator.translator': 
  957.         //     'translator.yzTranslator.translatorActive': 
  958.         //     'translator.xzTranslator.translatorActive': 
  959.         //     'translator.xyTranslator.translatorActive': 
  960.         // In the SoJackDragger, 'translator' is an SoDragPointDragger, 
  961.         // which takes care of all translations in 3 dimensions for jack.
  962.         // In the SoDragPointDragger there are 3 planar translation parts 
  963.         // (each is an SoTranslate2Dragger) and 3 linear translation parts 
  964.         // (each is an SoTranslate1Dragger).  At any given time, dragPoint
  965.         // displays one of each kind of dragger. We leave the linear 
  966.         // translators as the default geometry (a cylinder along the axis 
  967.         // of motion), but replace the geometry in the planar translators.
  968.         // Within the SoDragPointDragger, the planar translators are named
  969.         // 'yzTranslator', 'xzTranslator', and 'xyTranslator'.  
  970.         // Each of these is an SoTranslate2Dragger, which has two parts
  971.         // for its geometry, 'translator' and 'translatorActive.' Clicking
  972.         // on the 'translator' is what initiates the 2D translation.
  973.         // Once begun, the 'translatorActive' part is displayed. We
  974.         // replace both of these parts with a path to the selected object.
  975.  
  976.         // When we call setPartAsPath, we need to prune the path if our
  977.         // selected geometry lays inside a nodekit.
  978.         // For example, let's say a dumbBellKit contains 1 bar (a cylinder)
  979.         // and 2 end (spheres).  Since this is the lowest-level kit 
  980.         // containing these 3 shapes, they are considered a single object
  981.         // by the SceneViewer.
  982.         // So we need to pass a path to the kit, not the individual piece
  983.         // that is selected.  This way, subsequent clicks on any piece of 
  984.         // the dumbbell will cause 2D translation.
  985.         // First, is a nodekit on the path? If so, find the last one.
  986.         SoFullPath *jackP = (SoFullPath *) selectedPath;
  987.         SoType     bkType = SoBaseKit::getClassTypeId();
  988.         int        lastKitInd = -1;
  989.         for (int i = jackP->getLength() - 1; i >= 0; i--) {
  990.             if (jackP->getNode(i)->isOfType(bkType)) {
  991.             lastKitInd = i;
  992.             break;
  993.             }
  994.         }
  995.         // If there's a lastKitInd, make jackP be a copy of 
  996.         // selectedPath, but only up to lastKitInd.
  997.         if ( lastKitInd != -1)
  998.             jackP = (SoFullPath *) selectedPath->copy(0,lastKitInd + 1);
  999.  
  1000.         // Get the dragger from the manip (the manip contains the dragger,
  1001.         // and the dragger has the parts).
  1002.         SoDragger *d = theXfManip->getDragger();
  1003.  
  1004.         // Use jackP to set the translator parts, then discard (unref) it:
  1005.         jackP->ref();
  1006.         d->setPartAsPath("translator.yzTranslator.translator", jackP );
  1007.         d->setPartAsPath("translator.xzTranslator.translator", jackP );
  1008.         d->setPartAsPath("translator.xyTranslator.translator", jackP );
  1009.         d->setPartAsPath(
  1010.                 "translator.yzTranslator.translatorActive", jackP);
  1011.         d->setPartAsPath(
  1012.                 "translator.xzTranslator.translatorActive", jackP);
  1013.         d->setPartAsPath(
  1014.                 "translator.xyTranslator.translatorActive", jackP);
  1015.         jackP->unref();
  1016.     }
  1017.     }
  1018.  
  1019.     xfPath->unref();
  1020. }
  1021.  
  1022.  
  1023. ////////////////////////////////////////////////////////////////////////
  1024. //
  1025. // Description:
  1026. //    This routine detaches the manipulators from all selected objects.
  1027. //
  1028. // Use: private
  1029. void
  1030. SoSceneViewer::detachManipFromAll() 
  1031. //
  1032. ////////////////////////////////////////////////////////////////////////
  1033. {
  1034.     //
  1035.     // Loop from the end of the list to the start.
  1036.     //
  1037.     for (int i = selection->getNumSelected() - 1; i >= 0 ; i-- ) {
  1038.     SoPath *p = (SoPath *) (*selection)[i];
  1039.     detachManip( p );
  1040.     }
  1041. }
  1042.  
  1043.  
  1044. ////////////////////////////////////////////////////////////////////////
  1045. //
  1046. // Description:
  1047. //    This routine detaches a manipulator.  
  1048. //
  1049. // Use: private
  1050.  
  1051. void
  1052. SoSceneViewer::detachManip(
  1053.     SoPath *p )        // Selection object that is being removed
  1054. //
  1055. ////////////////////////////////////////////////////////////////////////
  1056. {
  1057.     //
  1058.     // Detach manip and remove from scene graph.
  1059.     //
  1060.     int which = maniplist->find(p);
  1061.     // See if this path is registered in the manip list.
  1062.     if (which != -1) {
  1063.     // remove from scene graph
  1064.     SoTransformManip *manip = maniplist->getManip(which);
  1065.  
  1066.     if ( manip->isOfType(SoTabBoxManip::getClassTypeId() )) {
  1067.         // Special case!  When using a  tab box, we want to adjust the
  1068.         // scale tabs upon viewer finish.
  1069.         currentViewer->removeFinishCallback( 
  1070.         &SoSceneViewer::adjustScaleTabSizeCB, manip->getDragger() );
  1071.     }
  1072.  
  1073.     SoPath *xfPath = maniplist->getXfPath(which);
  1074.     SoTransform *newXf = new SoTransform;
  1075.     newXf->ref();
  1076.     manip->ref();
  1077.  
  1078.     // replace the manip
  1079.     manip->replaceManip( xfPath, newXf );
  1080.  
  1081.     // If the transformSliderSet is attached to the manip, then attach 
  1082.     // it to the new node instead.
  1083.     if ( transformSliderSet && transformSliderSet->isVisible()
  1084.          && transformSliderSet->getNode() == manip)
  1085.         transformSliderSet->setNode(newXf);
  1086.  
  1087.     manip->unref();
  1088.     newXf->unref();
  1089.     
  1090.     // remove from maniplist
  1091.     maniplist->remove(which);
  1092.     }
  1093. }
  1094.  
  1095. ////////////////////////////////////////////////////////////////////////
  1096. //
  1097. // Description:
  1098. //    Added as a finish callback to the current viewer. It makes sure
  1099. //      the scale tab size gets changed when a viewer gesture is 
  1100. //      completed.
  1101. //
  1102. // Use: public
  1103. void
  1104. SoSceneViewer::adjustScaleTabSizeCB( void *userData, SoXtViewer *)
  1105. //
  1106. ////////////////////////////////////////////////////////////////////////
  1107. {
  1108.     SoTabBoxDragger *dragger = (SoTabBoxDragger *) userData;
  1109.     dragger->adjustScaleTabSize();
  1110. }
  1111.  
  1112. ////////////////////////////////////////////////////////////////////////
  1113. //
  1114. // Description:
  1115. //    See the selection from the camera
  1116. //
  1117. // Use: public
  1118. void
  1119. SoSceneViewer::viewSelection()
  1120. //
  1121. ////////////////////////////////////////////////////////////////////////
  1122. {
  1123.     if (selection->getNumSelected() == 0) {
  1124.     viewAll();
  1125.     return;
  1126.     }
  1127.     
  1128.     SoPath *path = (*selection)[0];
  1129.     if(path != NULL) {
  1130.     getCamera()->viewAll(path, currentViewer->getViewportRegion());
  1131.     }
  1132.     else {
  1133.     viewAll();
  1134.     return;
  1135.     }
  1136. }
  1137.  
  1138.  
  1139. ////////////////////////////////////////////////////////////////////////
  1140. //
  1141. // Description:
  1142. //    Create a color editor for the currently selected object.
  1143. //      Attachment code copied from SoXformManip.c++
  1144. //
  1145. // Use: private
  1146. void
  1147. SoSceneViewer::createColorEditor()
  1148. //
  1149. ////////////////////////////////////////////////////////////////////////
  1150. {
  1151.     if (colorEditor == NULL) {
  1152.     colorEditor = new MyColorEditor;
  1153.     colorEditor->setWYSIWYG(TRUE);
  1154.     colorEditor->setTitle("Diffuse Color");
  1155.     }
  1156.  
  1157.     SoMaterial *editMaterial = findMaterialForAttach( NULL );
  1158.     
  1159.     colorEditor->attach(&(editMaterial->diffuseColor), 0, editMaterial);
  1160.  
  1161.     colorEditor->show();
  1162. }
  1163.  
  1164.  
  1165. ////////////////////////////////////////////////////////////////////////
  1166. //
  1167. // Description:
  1168. //   Find the appropriate material node in the scene graph to attach a material
  1169. //   editor to.
  1170. //
  1171. //   Two possible cases:
  1172. //        [1] The path-tail is NOT a group.  We search the siblings of the path
  1173. //            tail (including the tail itself) from right to left for a node
  1174. //          that is affected by materials (shapes or groups).
  1175. //            We stop the search if we come to a material node to the left of the
  1176. //          pathTail.  If we find a node that IS affected by material, we will
  1177. //          insert a material node just before the path-tail. This is
  1178. //            because the editor should not affect nodes that appear
  1179. //            before attachPath in the scene graph.
  1180. //        [2] The path-tail IS a group.  We search the children from left to
  1181. //            right for material nodes.
  1182. //            We stop the search if we come to a material node.
  1183. //            If we find a node that is affected by materials, we will insert a
  1184. //          material just before this node. This is because the editor for a
  1185. //          group should affect ALL nodes within that group.
  1186. //
  1187. // NOTE: For the purposes of this routine, we consider SoSwitch as different
  1188. //       from other types of group. This is because we don't want to put
  1189. //       the new node underneath the switch, but next to it.
  1190. //
  1191. // Use: private
  1192. //
  1193. SoMaterial *
  1194. SoSceneViewer::findMaterialForAttach(
  1195.             const SoPath *target )    // path to start search from
  1196. //
  1197. ////////////////////////////////////////////////////////////////////////
  1198. {
  1199.     int         pathLength;
  1200.     SoPath         *selectionPath;
  1201.     SoMaterial        *editMtl;
  1202.  
  1203.     SbBool        madeNewMtl = FALSE; // did we create a new material
  1204.                         // node within this method?
  1205.  
  1206.  
  1207.     if ( ( selectionPath = (SoPath *) target ) == NULL ) {
  1208.     //
  1209.     //  If no selection path is specified, then use the LAST path in the
  1210.     //  current selection list.
  1211.     //
  1212.     selectionPath = (*selection)[selection->getNumSelected() - 1];    // last guy
  1213.     }
  1214.     pathLength = selectionPath->getLength();
  1215.  
  1216.     if ( pathLength <= 0 ) {
  1217.     fprintf( stderr, "No objects currently selected...\n" );
  1218.     return NULL;
  1219.     }
  1220.  
  1221. #ifdef DEBUG
  1222.     if ( pathLength < 2 ) {
  1223.     fprintf( stderr, "Picked object has no parent...\n" );
  1224.     return NULL;
  1225.     }
  1226. #endif
  1227.  
  1228.  
  1229.     // find 'group' and try to find 'editMtl'
  1230.     SoGroup     *group;
  1231.     SoNode      *node;
  1232.     int         index, i;
  1233.     SbBool    ignoreNodekit = FALSE;
  1234.  
  1235.     editMtl = NULL;
  1236.  
  1237.     if ( selectionPath->getTail()->isOfType( SoBaseKit::getClassTypeId() )) {
  1238.     // Nodekits have their own built in policy for creating new material
  1239.     // nodes. Allow them to contruct and return it.
  1240.     // Get the last nodekit in the path:
  1241.     SoBaseKit *kit = (SoBaseKit *) 
  1242.              ((SoNodeKitPath *)selectionPath)->getTail();
  1243.     // SO_CHECK_PART returns NULL if the part doesn't exist yet...
  1244.     editMtl = SO_GET_PART( kit, "material", SoMaterial );
  1245.     if ( editMtl == NULL ) {
  1246.         // This nodekit does not have a material part.
  1247.         // Ignore the fact that this is a nodekit.
  1248.         ignoreNodekit = TRUE;
  1249.     }
  1250.     }
  1251.  
  1252.     SbBool isTailGroup     = 
  1253.       selectionPath->getTail()->isOfType( SoGroup::getClassTypeId()) &&
  1254.       (!selectionPath->getTail()->isOfType( SoSwitch::getClassTypeId()));
  1255.     
  1256.     if ((editMtl == NULL) && ( !isTailGroup )) {
  1257.     //
  1258.         //    CASE 1: The path-tail is not a group.
  1259.         //    'group'      becomes the second to last node in the path.
  1260.         //    We search the path tail and its siblings from right to left for a
  1261.         //    mtl node.
  1262.         //    We stop the search if we come to a shape node or a group node
  1263.         //    to the left of the pathTail.  If we find a shape or group, we
  1264.         //    will insert a mtl just before the path-tail. This is
  1265.         //    because the manipulator should not affect objects that appear
  1266.         //    before selectionPath in the scene graph.
  1267.     //
  1268.         group      = (SoGroup *) selectionPath->getNode(pathLength - 2);
  1269.         index      = group->findChild( selectionPath->getTail() );
  1270.  
  1271.         for (i = index; (i >= 0) && (editMtl == NULL); i--){
  1272.             node = group->getChild(i);
  1273.             if (node->isOfType(SoMaterial::getClassTypeId())) // found SoMaterial
  1274.                 editMtl = (SoMaterial *) node;
  1275.             else if ( i != index ) { 
  1276.                 if ( isAffectedByMaterial( node ) )
  1277.                     break;
  1278.             }
  1279.         }
  1280.  
  1281.         if ( editMtl == NULL ) {
  1282.             editMtl = new SoMaterial;
  1283.             group->insertChild( editMtl, index );
  1284.         madeNewMtl = TRUE;
  1285.         }
  1286.     }
  1287.     else if (editMtl == NULL) {
  1288.         //    CASE 2: The path-tail is a group.
  1289.         //    'group'      becomes the path tail
  1290.         //      We search the children from left to right for mtl nodes.
  1291.         //      We stop the search if we come to a shape node or a group node.
  1292.         //      If we find a shape or group, we will insert a mtl just
  1293.         //      before this shape or group. This is because the editor
  1294.         //      for a group should affect ALL objects within that group.
  1295.     //
  1296.         group = (SoGroup *) selectionPath->getTail();
  1297.         for (i = 0; (i < group->getNumChildren()) && (editMtl == NULL); i++ ) {
  1298.             node = group->getChild(i);
  1299.             if (node->isOfType(SoMaterial::getClassTypeId()))
  1300.                 editMtl = (SoMaterial *) node;
  1301.             else if ( isAffectedByMaterial( node ) )
  1302.                 break;
  1303.         }
  1304.  
  1305.         if ( editMtl == NULL ) {
  1306.             editMtl = new SoMaterial;
  1307.             group->insertChild( editMtl, i );
  1308.         madeNewMtl = TRUE;
  1309.         }
  1310.     }
  1311.  
  1312.     // If we just created the material node here, then set the ignore
  1313.     // flags for all fields in the node.  This will cause the fields
  1314.     // to be inherited from their ancestors. The material editor will
  1315.     // undo these flags whenever it changes the value of a field
  1316.     if ( madeNewMtl == TRUE ) {
  1317.     editMtl->ambientColor.setIgnored( TRUE );
  1318.     editMtl->diffuseColor.setIgnored( TRUE );
  1319.     editMtl->specularColor.setIgnored( TRUE );
  1320.     editMtl->emissiveColor.setIgnored( TRUE );
  1321.     editMtl->shininess.setIgnored( TRUE );
  1322.     editMtl->transparency.setIgnored( TRUE );
  1323.     }
  1324.  
  1325.     // If any of the fields is ignored, then fill the value with the value
  1326.     // inherited from the rest of the scene graph
  1327.     if ( editMtl->ambientColor.isIgnored() 
  1328.     || editMtl->diffuseColor.isIgnored() 
  1329.     || editMtl->specularColor.isIgnored() 
  1330.     || editMtl->emissiveColor.isIgnored() 
  1331.     || editMtl->shininess.isIgnored() 
  1332.     || editMtl->transparency.isIgnored() ){
  1333.  
  1334.     // Create a path to the material
  1335.     SoPath *mtlPath;
  1336.     if ( (! ignoreNodekit) && selectionPath->getTail()->isOfType( SoBaseKit::getClassTypeId() )) {
  1337.         SoBaseKit *kit = (SoBaseKit *) 
  1338.                  ((SoNodeKitPath *)selectionPath)->getTail();
  1339.         mtlPath = kit->createPathToPart( "material", TRUE, selectionPath );
  1340.         mtlPath->ref();
  1341.     }
  1342.     else {
  1343.         if ( !isTailGroup ) {
  1344.         // CASE 1: path-tail was NOT 'group' -- copy all but last entry
  1345.         mtlPath = selectionPath->copy(0, pathLength - 1);
  1346.         }
  1347.         else {
  1348.         // CASE 2: path-tail was 'group' -- copy all of editPath
  1349.         mtlPath = selectionPath->copy(0, pathLength);
  1350.         }
  1351.         mtlPath->ref();
  1352.         // add the material to the end of the path
  1353.         int mtlIndex    = group->findChild(editMtl);
  1354.         mtlPath->append( mtlIndex );
  1355.     }
  1356.  
  1357.     // Pass the material node to an accumulate state callback
  1358.     // that will load any 'ignored' values with their inherited values.
  1359.     SoCallbackAction cba;
  1360.     cba.addPreTailCallback( SoSceneViewer::findMtlPreTailCB, editMtl);
  1361.     cba.apply( mtlPath );
  1362.  
  1363.     mtlPath->unref();
  1364.     }
  1365.  
  1366.  
  1367.     return( editMtl );
  1368. }
  1369.  
  1370. ////////////////////////////////////////////////////////////////////////
  1371. //
  1372. // Description:
  1373. //   Callback used by 'findMaterialForAttach' as part of the accumulate state
  1374. //   action. Returns 'PRUNE', which tells the action not to draw the
  1375. //   shape as part of the accum state action.
  1376. //   editor to.
  1377. //
  1378. // Use: private
  1379. //
  1380. SoCallbackAction::Response
  1381. SoSceneViewer::findMtlPreTailCB( void *data, SoCallbackAction *accum, 
  1382.                  const SoNode * )
  1383. //
  1384. ////////////////////////////////////////////////////////////////////////
  1385. {
  1386.     SoMaterial *mtl = (SoMaterial *) data;
  1387.  
  1388.     SbColor ambient, diffuse, specular, emissive;
  1389.     float   shininess, transparency;
  1390.  
  1391.     accum->getMaterial( ambient, diffuse, specular, emissive, 
  1392.             shininess, transparency ); 
  1393.  
  1394.     // inherit the accumulated values only in those fields being ignored.
  1395.     if ( mtl->ambientColor.isIgnored() )
  1396.      mtl->ambientColor.setValue( ambient );
  1397.     if ( mtl->diffuseColor.isIgnored() )
  1398.      mtl->diffuseColor.setValue( diffuse );
  1399.     if ( mtl->specularColor.isIgnored() )
  1400.      mtl->specularColor.setValue( specular );
  1401.     if ( mtl->emissiveColor.isIgnored() )
  1402.      mtl->emissiveColor.setValue( emissive );
  1403.     if ( mtl->shininess.isIgnored() )
  1404.      mtl->shininess.setValue( shininess );
  1405.     if ( mtl->transparency.isIgnored() )
  1406.      mtl->transparency.setValue( transparency );
  1407.  
  1408.     return SoCallbackAction::ABORT;
  1409. }
  1410.  
  1411. ////////////////////////////////////////////////////////////////////////
  1412. //
  1413. // Description:
  1414. //   Find the appropriate transform node in the scene graph for attaching a 
  1415. //   transform editor or manipulator.
  1416. //
  1417. //   How we treat the 'center' field of the transform node:
  1418. //   If we need to create a new transform node:
  1419. //       set the 'center' to be the geometric center of all objects 
  1420. //       affected by that transform. 
  1421. //   If we find a transform node that already exists:
  1422. //       'center' will not be changed.
  1423. //
  1424. //   Three possible cases:
  1425. //        [1] The path-tail is a node kit. Just ask the node kit for a path
  1426. //            to the part called "transform"
  1427. //        [2] The path-tail is NOT a group.  We search the siblings of the path
  1428. //            tail (including the tail itself) from right to left for a node
  1429. //          that is affected by transforms (shapes, groups, lights,cameras).
  1430. //            We stop the search if we come to a transform node to the left of 
  1431. //          the pathTail.  If we find a node that IS affected by transform, 
  1432. //          we will insert a transform node just before the path-tail. This is
  1433. //            because the editor should not affect nodes that appear
  1434. //            before attachPath in the scene graph.
  1435. //        [3] The path-tail IS a group.  We search the children from left to
  1436. //            right for transform nodes.
  1437. //            We stop the search if we come to a transform node.
  1438. //            If we find a node that is affected by transform, we will insert a
  1439. //          transform just before this node. This is because the editor for a
  1440. //          group should affect ALL nodes within that group.
  1441. //
  1442. // NOTE: For the purposes of this routine, we consider SoSwitch as different
  1443. //       from other types of group. This is because we don't want to put
  1444. //       the new node underneath the switch, but next to it.
  1445. //
  1446. // Use: private
  1447. //
  1448. SoPath *
  1449. SoSceneViewer::findTransformForAttach(
  1450.             const SoPath *target )    // path to start search from
  1451. //
  1452. ////////////////////////////////////////////////////////////////////////
  1453. {
  1454.     int         pathLength;
  1455.     SoPath         *selectionPath;
  1456.     SoTransform        *editXform;
  1457.  
  1458.     
  1459.     if ( ( selectionPath = (SoPath *) target ) == NULL ) {
  1460.     //
  1461.     //  If no selection path is specified, then use the LAST path in the
  1462.     //  current selection list.
  1463.     //
  1464.     selectionPath = (*selection)[selection->getNumSelected() - 1];
  1465.     }
  1466.     pathLength = selectionPath->getLength();
  1467.  
  1468.     if ( pathLength <= 0 ) {
  1469.     fprintf( stderr, "No objects currently selected...\n" );
  1470.     return NULL;
  1471.     }
  1472.  
  1473. #ifdef DEBUG
  1474.     if ( pathLength < 2 ) {
  1475.     fprintf( stderr, "Picked object has no parent...\n" );
  1476.     return NULL;
  1477.     }
  1478. #endif
  1479.  
  1480.     // find 'group' and try to find 'editXform'
  1481.     SoGroup     *group;
  1482.     SoNode      *node;
  1483.     int         index, i;
  1484.     SbBool      isTailGroup,  isTailKit;
  1485.     SbBool      existedBefore = FALSE;
  1486.     SoPath      *pathToXform = NULL;
  1487.  
  1488.     editXform = NULL;
  1489.  
  1490.     isTailGroup =
  1491.         (   selectionPath->getTail()->isOfType(SoGroup::getClassTypeId() )
  1492.         && !selectionPath->getTail()->isOfType(SoSwitch::getClassTypeId()));
  1493.  
  1494.     isTailKit = selectionPath->getTail()->isOfType(SoBaseKit::getClassTypeId());
  1495.  
  1496.     //    CASE 1: The path-tail is a node kit.
  1497.     if ( isTailKit ) {
  1498.  
  1499.     // Nodekits have their own built in policy for creating new transform
  1500.     // nodes. Allow them to contruct and return a path to it.
  1501.     SoBaseKit *kit = (SoBaseKit *) 
  1502.              ((SoNodeKitPath *)selectionPath)->getTail();
  1503.  
  1504.     // Before creating path, see if the transform part exists yet:
  1505.     if (SO_CHECK_PART(kit, "transform", SoTransform) != NULL)
  1506.         existedBefore = TRUE;
  1507.  
  1508.     if ((editXform = SO_GET_PART(kit, "transform", SoTransform)) != NULL) {
  1509.         pathToXform = kit->createPathToPart( "transform", TRUE, selectionPath );
  1510.         pathToXform->ref();
  1511.     }
  1512.     else {
  1513.         // This nodekit has no transform part. 
  1514.         // Treat the object as if it were not a nodekit.
  1515.         isTailKit = FALSE;
  1516.     }
  1517.     }
  1518.     
  1519.     if ( !isTailGroup && !isTailKit ) {
  1520.     //
  1521.         //    CASE 2: The path-tail is not a group.
  1522.         //    'group'      becomes the second to last node in the path.
  1523.         //    We search the path tail and its siblings from right to left for a
  1524.         //    transform node.
  1525.         //    We stop the search if we come to a 'movable' node
  1526.         //    to the left of the pathTail.  If we find a movable node, we
  1527.         //    will insert a transform just before the path-tail. This is
  1528.         //    because the manipulator should not affect objects that appear
  1529.         //    before selectionPath in the scene graph.
  1530.     //
  1531.         group      = (SoGroup *) selectionPath->getNode(pathLength - 2);
  1532.         index      = group->findChild( selectionPath->getTail() );
  1533.  
  1534.         for (i = index; (i >= 0) && (editXform == NULL); i--){
  1535.             node = group->getChild(i);
  1536.             if (node->isOfType(SoTransform::getClassTypeId()))  // found an SoMaterial
  1537.                 editXform = (SoTransform *) node;
  1538.             else if ( i != index ) { 
  1539.         if ( isAffectedByTransform( node ) )
  1540.             break;
  1541.             }
  1542.         }
  1543.  
  1544.         if ( editXform == NULL ) {
  1545.         existedBefore = FALSE;
  1546.             editXform = new SoTransform;
  1547.             group->insertChild( editXform, index );
  1548.         }
  1549.     else
  1550.         existedBefore = TRUE;
  1551.     }
  1552.     else if ( !isTailKit ) {
  1553.         //    CASE 3: The path-tail is a group.
  1554.         //    'group'      becomes the path tail
  1555.         //      We search the children from left to right for transform nodes.
  1556.         //      We stop the search if we come to a movable node.
  1557.         //      If we find a movable node, we will insert a transform just
  1558.         //      before this node. This is because the editor
  1559.         //      for a group should affect ALL objects within that group.
  1560.     //
  1561.         group = (SoGroup *) selectionPath->getTail();
  1562.         for (i = 0; (i < group->getNumChildren()) && (editXform == NULL); i++ ) {
  1563.             node = group->getChild(i);
  1564.             if (node->isOfType(SoTransform::getClassTypeId()))
  1565.                 editXform = (SoTransform *) node;
  1566.             else if ( isAffectedByTransform( node ) )
  1567.                 break;
  1568.         }
  1569.  
  1570.         if ( editXform == NULL ) {
  1571.         existedBefore = FALSE;
  1572.             editXform = new SoTransform;
  1573.             group->insertChild( editXform, i );
  1574.         }
  1575.     else
  1576.         existedBefore = TRUE;
  1577.     }
  1578.  
  1579.     // If we don't have a path yet (i.e., we weren't handed a nodekit path)
  1580.     // create the 'pathToXform'
  1581.     // by copying editPath and making the last node in the path be editXform
  1582.     if ( pathToXform == NULL ) {
  1583.     if ( !isTailGroup )
  1584.         // CASE 2: path-tail was NOT 'group' -- copy all but last entry
  1585.         pathToXform = selectionPath->copy(0, pathLength - 1);
  1586.     else
  1587.         // CASE 3: path-tail was 'group' -- copy all of editPath
  1588.         pathToXform = selectionPath->copy(0, pathLength);
  1589.     pathToXform->ref();
  1590.  
  1591.     // add the transform to the end
  1592.     int xfIndex    = group->findChild(editXform);
  1593.     pathToXform->append( xfIndex );
  1594.     }
  1595.  
  1596.  
  1597.     // Now. If we created the transform node right here, right now, then
  1598.     // we will set the 'center' field based on the geometric center. We 
  1599.     // don't do this if we didn't create the transform, because "maybe it
  1600.     // was that way for a reason."
  1601.     if ( existedBefore == FALSE ) {
  1602.     // First, find 'applyPath' by popping nodes off the path until you 
  1603.     // reach a separator. This path will contain all nodes affected by
  1604.     // the transform at the end of 'pathToXform'
  1605.     SoFullPath *applyPath = (SoFullPath *) pathToXform->copy();
  1606.     applyPath->ref();
  1607.     for (int i = (applyPath->getLength() - 1); i >0; i-- ) {
  1608.         if (applyPath->getNode(i)->isOfType( SoSeparator::getClassTypeId()))
  1609.         break;
  1610.         applyPath->pop();
  1611.     }
  1612.  
  1613.     // Next, apply a bounding box action to applyPath, and reset the
  1614.     // bounding box just before the tail of 'pathToXform' (which is just
  1615.     // the editXform). This will assure that the only things included in 
  1616.     // the resulting bbox will be those affected by the editXform.
  1617.     SoGetBoundingBoxAction bboxAction(currentViewer->getViewportRegion());
  1618.     bboxAction.setResetPath(pathToXform,TRUE,SoGetBoundingBoxAction::BBOX );
  1619.     bboxAction.apply(applyPath);
  1620.  
  1621.     applyPath->unref();
  1622.  
  1623.     // Get the center of the bbox in world space...
  1624.     SbVec3f worldBoxCenter = bboxAction.getBoundingBox().getCenter();
  1625.  
  1626.     // Convert it into local space of the transform...
  1627.     SbVec3f localBoxCenter;
  1628.     SoGetMatrixAction ma(currentViewer->getViewportRegion());
  1629.     ma.apply( pathToXform );
  1630.     ma.getInverse().multVecMatrix( worldBoxCenter, localBoxCenter );
  1631.  
  1632.     // Finally, set the center value...
  1633.     editXform->center.setValue( localBoxCenter );
  1634.     }
  1635.  
  1636.     pathToXform->unrefNoDelete();
  1637.     return( pathToXform );
  1638. }
  1639.  
  1640. ////////////////////////////////////////////////////////////////////////
  1641. //
  1642. // SPACEBALL
  1643. // Description:
  1644. //      Handle events in the TransformScaleEditor.
  1645. //      
  1646. //
  1647. // Use: private, static
  1648. void 
  1649. SoSceneViewer::transformScaleCallback(void *userData, float val) {
  1650. //
  1651. ////////////////////////////////////////////////////////////////////////
  1652.     SoSceneViewer *sv = (SoSceneViewer *) userData;
  1653.         if (sv->spaceball == NULL) {
  1654.         return;
  1655.     }
  1656.     sv->transformScaleFactor = val;
  1657.     if (val < .5) {
  1658.         val = 1 - ((.5 - val) * 2);
  1659.     } else {
  1660.         val = 1 + ((val - .5)  * 2);
  1661.     }
  1662.     sv->transformScaleFactor = val;
  1663.     sv->spaceball->setTranslationScaleFactor(
  1664.         (sv->transformScaleFactor*sv->average)/10000);
  1665.  
  1666. }
  1667.  
  1668. ////////////////////////////////////////////////////////////////////////
  1669. //
  1670. // SPACEBALL
  1671. // Description:
  1672. //      Create a slider that will let the user set the speed of translation
  1673. //      for the spaceball.
  1674. //
  1675. // Use: private
  1676. void
  1677. SoSceneViewer::createTransformScaleEditor() {
  1678. //
  1679. ////////////////////////////////////////////////////////////////////////
  1680.     int        n;
  1681.     Arg        args[10];
  1682.  
  1683.     // Create a dialog shell, and a form to hold slider and labels.
  1684.     n = 0;
  1685.     XtSetArg(args[n], XmNwidth, 200); n++;
  1686.     XtSetArg(args[n], XmNheight, 50); n++;
  1687.     Widget dialogShell = XmCreateDialogShell(XtParent(mgrWidget), "Inventor", 
  1688.         args, n);
  1689.     Widget form = XtVaCreateManagedWidget ("form", xmFormWidgetClass,
  1690.                 dialogShell, 
  1691.                 XmNwidth, 200,
  1692.                 XmNheight, 60, NULL);
  1693.     registerWidget(form);
  1694.     // Create the slider.  The "MySlider" class is defined in
  1695.     // libInventorWidget.a, in /usr/share/src/Inventor/samples/widgets.
  1696.     transformScaleSlider = new MySlider(form,
  1697.             "Transform Scale Slider", TRUE);
  1698.  
  1699.     // Create the labels
  1700.     XmString  leftString = XmStringCreateLtoR ("Slow",
  1701.                 XmSTRING_DEFAULT_CHARSET);
  1702.     XmString  rightString = XmStringCreateLtoR ("Fast",
  1703.                 XmSTRING_DEFAULT_CHARSET);
  1704.     XmString  topString = XmStringCreateLtoR ("Spaceball Translation Speed",
  1705.                 XmSTRING_DEFAULT_CHARSET);
  1706.     Widget topLabel = XtVaCreateManagedWidget ("SpaceballTransSPeed",
  1707.                     xmLabelGadgetClass,
  1708.                     form,
  1709.                                         XmNlabelString, topString,
  1710.                     XmNleftAttachment, XmATTACH_FORM,
  1711.                     XmNrightAttachment, XmATTACH_FORM,
  1712.                                         XmNtopAttachment, XmATTACH_FORM,
  1713.                     NULL);
  1714.     Widget right_label = XtVaCreateManagedWidget ("Fast",
  1715.                                         xmLabelGadgetClass,
  1716.                                         form, 
  1717.                                         XmNlabelString, rightString,
  1718.                                         XmNrightAttachment, XmATTACH_FORM,
  1719.                                         XmNtopAttachment, XmATTACH_WIDGET,
  1720.                     XmNtopWidget, topLabel,
  1721.                                         XmNwidth, 50,
  1722.                                         XmNalignment, XmALIGNMENT_END,
  1723.                                         NULL);
  1724.     Widget left_label = XtVaCreateManagedWidget ("Slow",
  1725.                                         xmLabelGadgetClass,
  1726.                                         form, 
  1727.                                         XmNlabelString, leftString,
  1728.                                         XmNleftAttachment, XmATTACH_FORM,
  1729.                                         XmNtopAttachment, XmATTACH_WIDGET,
  1730.                     XmNtopWidget, topLabel,
  1731.                                         XmNwidth, 50,
  1732.                                         XmNalignment, XmALIGNMENT_BEGINNING,
  1733.                                         NULL);
  1734.     // Get the slider set up in the right place.
  1735.     n=0;
  1736.     XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  1737.     XtSetArg(args[n], XmNleftWidget, left_label); n++;
  1738.     XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
  1739.     XtSetArg(args[n], XmNrightWidget, right_label); n++;
  1740.     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  1741.     XtSetArg(args[n], XmNtopWidget, topLabel); n++;
  1742.     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
  1743.     XtSetValues(transformScaleSlider->getWidget(), args, n);
  1744.     transformScaleSlider->addValueChangedCallback(transformScaleCallback,
  1745.         this);
  1746.     transformScaleSlider->setNumericFieldVisible(FALSE);
  1747.     transformScaleSlider->setValue(transformScaleFactor);
  1748.  
  1749.     // Pop up the slider widget
  1750.     transformScaleSlider->show();
  1751.     XtManageChild(dialogShell);
  1752. }
  1753.  
  1754.  
  1755. ////////////////////////////////////////////////////////////////////////
  1756. //
  1757. // Description:
  1758. //    Create a material editor for the currently selected object.
  1759. //
  1760. // Use: private
  1761. void
  1762. SoSceneViewer::createMaterialEditor()
  1763. //
  1764. ////////////////////////////////////////////////////////////////////////
  1765. {
  1766.     if (materialEditor == NULL)
  1767.         materialEditor = new SoXtMaterialEditor;
  1768.     materialEditor->show();
  1769.     
  1770.     materialEditor->attach( findMaterialForAttach( NULL ) );
  1771. }
  1772.  
  1773. ////////////////////////////////////////////////////////////////////////
  1774. //
  1775. // Description:
  1776. //    Create a transform editor for the currently selected object
  1777. //
  1778. // Use: private
  1779. void
  1780. SoSceneViewer::createTransformSliderSet()
  1781. //
  1782. ////////////////////////////////////////////////////////////////////////
  1783. {
  1784.     SoPath      *editTransformPath;
  1785.     SoTransform *editTransform;
  1786.  
  1787.  
  1788.     // get path to a transform to edit
  1789.     if ( ( editTransformPath = findTransformForAttach( NULL )) == NULL )
  1790.     return;
  1791.     xform = (SoTransform *) ((SoFullPath *)
  1792.                 editTransformPath)->getTail();
  1793.  
  1794.     // the tail of the path is a transform for us!
  1795.     editTransformPath->ref();
  1796.     editTransform =(SoTransform *) ((SoFullPath *)editTransformPath)->getTail();
  1797.     editTransformPath->unref();
  1798.     
  1799.     // Nuke the old slider set and get a new one
  1800.     if (transformSliderSet == NULL)
  1801.     transformSliderSet = new SoXtTransformSliderSet();
  1802.     transformSliderSet->setNode(editTransform);
  1803.     transformSliderSet->show();
  1804. }
  1805.  
  1806. ////////////////////////////////////////////////////////////////////////
  1807. //
  1808. // Description:
  1809. //      Set fog on/off. Leave density alone - when default, the environment
  1810. // node will do something useful to make the fog look good.
  1811. //
  1812. // Use: private
  1813. void
  1814. SoSceneViewer::setFog(SbBool flag)
  1815. //
  1816. ////////////////////////////////////////////////////////////////////////
  1817. {
  1818.     fogFlag = flag;
  1819.     
  1820.     if (fogFlag)
  1821.          environment->fogType.setValue( SoEnvironment::HAZE ); // purple ?
  1822.     else environment->fogType.setValue( SoEnvironment::NONE );
  1823. }
  1824.  
  1825. ////////////////////////////////////////////////////////////////////////
  1826. //
  1827. // Description:
  1828. //      Set AA-ing on/off.
  1829. //
  1830. // Use: private
  1831. void
  1832. SoSceneViewer::setAntialiasing(SbBool flag)
  1833. //
  1834. ////////////////////////////////////////////////////////////////////////
  1835. {
  1836.     antialiasingFlag = flag;
  1837.     
  1838.     if (antialiasingFlag)
  1839.     currentViewer->setAntialiasing( TRUE, 3 );
  1840.     else
  1841.     currentViewer->setAntialiasing( FALSE, 1 );
  1842. }
  1843.  
  1844. ////////////////////////////////////////////////////////////////////////
  1845. //
  1846. // Description:
  1847. //      Invokes color editor on ambient lighting color.
  1848. void
  1849. SoSceneViewer::editAmbientColor()
  1850. //
  1851. ////////////////////////////////////////////////////////////////////////
  1852.     if ( ambientColorEditor == NULL ) {
  1853.         ambientColorEditor = new MyColorEditor;
  1854.     ambientColorEditor->setTitle( "Ambient Lighting" );
  1855.     ambientColorEditor->addColorChangedCallback(
  1856.         SoSceneViewer::ambientColorCallback, this );
  1857.     }
  1858.     
  1859.     // Normalize ambient intensity
  1860.     SbColor ambCol;
  1861.     ambCol = environment->ambientColor.getValue();
  1862.     ambCol *= environment->ambientIntensity.getValue();
  1863.     environment->ambientIntensity.setValue( 1.0 );
  1864.     environment->ambientColor.setValue( ambCol );
  1865.     
  1866.     ignoreCallback = TRUE;
  1867.     ambientColorEditor->setColor( environment->ambientColor.getValue() );
  1868.     ignoreCallback = FALSE;
  1869.     ambientColorEditor->show();
  1870. }
  1871.  
  1872. ////////////////////////////////////////////////////////////////////////
  1873. //
  1874. // Description:
  1875. //  Callback proc invoked by the color editor, this changes the scene's
  1876. //  ambient lighting color.
  1877. //
  1878. //  Use: static, private
  1879. //
  1880. void
  1881. SoSceneViewer::ambientColorCallback(void *userData, const SbColor *color)
  1882. //
  1883. ////////////////////////////////////////////////////////////////////////
  1884. {
  1885.     SoSceneViewer *sv = (SoSceneViewer *) userData;
  1886.     
  1887.     if (sv->ignoreCallback)
  1888.         return;
  1889.     
  1890.     sv->environment->ambientColor.setValue( *color );
  1891. }
  1892.  
  1893. ////////////////////////////////////////////////////////////////////////
  1894. //
  1895. // Description:
  1896. //      Invokes color editor on background color.
  1897. void
  1898. SoSceneViewer::editBackgroundColor()
  1899. //
  1900. ////////////////////////////////////////////////////////////////////////
  1901.     if ( backgroundColorEditor == NULL ) {
  1902.         backgroundColorEditor = new MyColorEditor;
  1903.     backgroundColorEditor->setTitle( "Background Color" );
  1904.     backgroundColorEditor->addColorChangedCallback(
  1905.         SoSceneViewer::backgroundColorCallback, this );
  1906.     }
  1907.     ignoreCallback = TRUE;
  1908.     backgroundColorEditor->setColor(getBackgroundColor());
  1909.     ignoreCallback = FALSE;
  1910.     backgroundColorEditor->show();
  1911. }
  1912.  
  1913. ////////////////////////////////////////////////////////////////////////
  1914. //
  1915. // Description:
  1916. //  Callback proc invoked by the color editor, this changes the current
  1917. //  viewer's background color.
  1918. //
  1919. //  Use: static, private
  1920. //
  1921. void
  1922. SoSceneViewer::backgroundColorCallback(void *userData, const SbColor *c)
  1923. //
  1924. ////////////////////////////////////////////////////////////////////////
  1925. {
  1926. //??? should be using the Roxy color editor, not the So color editor
  1927.  
  1928.     SoSceneViewer *sv = (SoSceneViewer *) userData;
  1929.     
  1930.     if (sv->ignoreCallback)
  1931.         return;
  1932.     
  1933.     sv->currentViewer->setBackgroundColor( *c );
  1934.     
  1935.     // keep fog color up to date with bkg color
  1936.     sv->environment->fogColor.setValue( *c );
  1937. }
  1938.  
  1939. ////////////////////////////////////////////////////////////////////////
  1940. //
  1941. // Description:
  1942. //    This will remove any cameras under root.
  1943. //
  1944. // Use: private
  1945. void
  1946. SoSceneViewer::removeCameras(SoGroup *root)
  1947. //
  1948. ////////////////////////////////////////////////////////////////////////
  1949. {
  1950.     SoSearchAction sa;
  1951.     sa.setType(SoCamera::getClassTypeId());
  1952.     sa.setInterest(SoSearchAction::ALL);
  1953.     sa.apply(root);
  1954.     
  1955.     // remove those cameras!
  1956.     SoPathList paths = sa.getPaths();
  1957.     for (int i = 0; i < paths.getLength(); i++) {
  1958.     SoPath *p = paths[i];
  1959.     SoCamera *cam = (SoCamera *) p->getNodeFromTail(0);
  1960.     SoGroup  *group = (SoGroup *) p->getNodeFromTail(1);
  1961.     group->removeChild(cam);
  1962.     }
  1963. }
  1964.  
  1965. ////////////////////////////////////////////////////////////////////////
  1966. //
  1967. // Description:
  1968. //    Reads the given file and insert the geometry under the selection
  1969. //  node. If the node didn't have any children, the viewAll() method is
  1970. //  automatically called.
  1971. //
  1972. // Use: private
  1973. SbBool
  1974. SoSceneViewer::readFile(const char *filename)
  1975. //
  1976. ////////////////////////////////////////////////////////////////////////
  1977. {
  1978.     SoInput in;
  1979.     if (! in.openFile(filename)) {
  1980.     
  1981.     // display an error dialog
  1982.     char str[100];
  1983.     strcpy(str, "Error opening file: ");
  1984.     strcat(str, filename);
  1985.     SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str);
  1986.     return FALSE;
  1987.     }
  1988.     
  1989.     SbBool hadNoChildren = (selection->getNumChildren() == 0);
  1990.     
  1991.     // add nodes under selection, not sceneGraph
  1992.     SoNode *node;
  1993.     SbBool ok;
  1994.     while ((ok = SoDB::read(&in, node)) && (node != NULL))
  1995.     selection->addChild(node);
  1996.     
  1997.     // display error dialog if there were reading errors
  1998.     if (!ok) {
  1999.     char str[100];
  2000.     strcpy(str, "Error reading file: ");
  2001.     strcat(str, filename);
  2002.     SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str);
  2003.     return FALSE;
  2004.     }
  2005.     
  2006.     // remove any cameras under selection which were just added
  2007.     removeCameras(selection);
  2008.     if (selection->getChild(0) != NULL) {
  2009.         fixDXFSceneGraph((SoGroup *) (selection->getChild(0)));
  2010.     }
  2011.     setupSpaceball();
  2012.  
  2013.     
  2014.     if (hadNoChildren) {
  2015.     viewAll();
  2016.     saveHomePosition();
  2017.     }
  2018.     
  2019.     return TRUE;
  2020. }
  2021.  
  2022. ////////////////////////////////////////////////////////////////////////
  2023. //
  2024. // Description:
  2025. //    Read environment data. We expect the following nodes:
  2026. //
  2027. //  Group {
  2028. //    Label { "SoSceneViewer Environment v3.0" }
  2029. //    Camera {}
  2030. //    Environment {}
  2031. //    LightGroup {
  2032. //      Switch { DirectionalLight }  # 1
  2033. //      ...
  2034. //      Switch { DirectionalLight }  # 6
  2035. //    }
  2036. //    DirectionalLight {}       # optional headlight
  2037. //  }
  2038. //
  2039. // Use: private
  2040. SbBool
  2041. SoSceneViewer::readEnvFile(const char *filename)
  2042. //
  2043. ////////////////////////////////////////////////////////////////////////
  2044. {
  2045.     SoInput in;
  2046.     if (! in.openFile(filename)) {
  2047.     // display an error dialog
  2048.     char str[100];
  2049.     strcpy(str, "Error opening file: ");
  2050.     strcat(str, filename);
  2051.     SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str);
  2052.     return FALSE;
  2053.     }
  2054.     
  2055.     SoNode *n;
  2056.     SoGroup *g = NULL;
  2057.     SoLabel *l = NULL;
  2058.     SbBool isValid = FALSE;
  2059.     SbBool ok;
  2060.     
  2061.     if ((ok = SoDB::read(&in, n)) && n != NULL) {
  2062.     // we expect a label first
  2063.     n->ref();
  2064.     if (n->isOfType(SoLabel::getClassTypeId())) {
  2065.         l = (SoLabel *) n;
  2066.         isValid = (strcmp(l->label.getValue().getString(),
  2067.                   SV_ENV_LABEL) == 0);
  2068.     }
  2069.     n->unref();
  2070.     }
  2071.     else if (!ok) {
  2072.     // display error dialog if there were reading errors
  2073.     char str[100];
  2074.     strcpy(str, "Error reading file: ");
  2075.     strcat(str, filename);
  2076.     SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str);
  2077.     return FALSE;
  2078.     }
  2079.  
  2080.     // if ok, read the rest.
  2081.     if (isValid) {
  2082.     // Camera
  2083.     if (SoDB::read(&in, n) != FALSE && (n != NULL)) {
  2084.         n->ref();
  2085.         if (n->isOfType(SoCamera::getClassTypeId())) {
  2086.         // replace the old camera with the new camera and
  2087.         // re-attach the viewer.
  2088.         SoCamera *newCamera = (SoCamera *) n;
  2089.         SoCamera *oldCamera = getCamera();
  2090.         SoSearchAction sa;
  2091.         sa.setNode(oldCamera);
  2092.         sa.apply(sceneGraph);
  2093.         SoFullPath *fullCamPath = (SoFullPath *) sa.getPath();
  2094.         if (fullCamPath) {
  2095.             SoGroup *parent = 
  2096.             (SoGroup *)fullCamPath->getNode(fullCamPath->getLength() - 2);
  2097.             parent->insertChild(newCamera, parent->findChild(oldCamera));
  2098.             setCamera(newCamera);
  2099.             if (parent->findChild(oldCamera) >= 0)
  2100.             parent->removeChild(oldCamera);
  2101.         }
  2102. #if DEBUG
  2103.         else
  2104.             SoDebugError::post("SoSceneViewer::readEnvFile",
  2105.                 "cannot find camera in graph");
  2106. #endif
  2107.         }
  2108.         n->unref();
  2109.     }
  2110.     // Environment
  2111.     if (SoDB::read(&in, n) != FALSE && (n != NULL)) {
  2112.         n->ref();
  2113.         if (n->isOfType(SoEnvironment::getClassTypeId())) {
  2114.         lightsCameraEnvironment->replaceChild(environment, n);
  2115.         environment = (SoEnvironment *) n;
  2116.         }
  2117.         n->unref();
  2118.     }
  2119.     // Light group
  2120.     if (SoDB::read(&in, n) != FALSE && (n != NULL)) {
  2121.         n->ref();
  2122.         if (n->isOfType(SoGroup::getClassTypeId())) {
  2123.         
  2124.         // remove all of the existing lights
  2125.         for (int i = lightDataList.getLength(); i > 0; i--)
  2126.             removeLight( (SvLightData *) lightDataList[i-1] );
  2127.         
  2128.         lightsCameraEnvironment->replaceChild(lightGroup, n);
  2129.         lightGroup = (SoGroup *) n;
  2130.         
  2131.         // This was busted. It was looking for a light as child 0,
  2132.         // but the scale and scaleInverse made it think no light was 
  2133.         // there. So now, we do this right...
  2134.         // We'll just check for the light as any old child.
  2135.         // This way it's okay to add a translation node under that 
  2136.         // switch too, so we can translate the manips as well.
  2137.         // This allows as to place the directional light manips.
  2138.         for (i=0; i < lightGroup->getNumChildren(); i++) {
  2139.             SoNode *node = lightGroup->getChild(i);
  2140.             if (node->isOfType(SoSwitch::getClassTypeId())) {
  2141.             SoSwitch *sw = (SoSwitch *) node;
  2142.             SbBool addedIt = FALSE;
  2143.             for (int j = 0; 
  2144.                  addedIt == FALSE && j < sw->getNumChildren(); 
  2145.                  j++ ) {
  2146.                 node = sw->getChild(j);
  2147.                 if (node->isOfType(SoLight::getClassTypeId())) {
  2148.                 addLightEntry((SoLight *)node, sw);
  2149.                 addedIt = TRUE;
  2150.                 }
  2151.             }
  2152.             }
  2153.         }
  2154.         }
  2155.         n->unref();
  2156.     }
  2157.     // Headlight (optional) - if not there, turn headlight off
  2158.     if (SoDB::read(&in, n) != FALSE && (n != NULL)) {
  2159.         n->ref();
  2160.         if (n->isOfType(SoDirectionalLight::getClassTypeId())) {
  2161.         SoDirectionalLight *headlight = getHeadlight();
  2162.         SoDirectionalLight *newLight = (SoDirectionalLight *) n;
  2163.         if (headlight != NULL) {
  2164.             headlight->intensity.setValue(newLight->intensity.getValue());
  2165.             headlight->color.setValue(newLight->color.getValue());
  2166.             headlight->direction.setValue(newLight->direction.getValue());
  2167.             setHeadlight(TRUE);
  2168.         }
  2169.         }
  2170.         n->unref();
  2171.     }
  2172.     else setHeadlight(FALSE);
  2173.     }
  2174.     else {
  2175.     fprintf(stderr, "Sorry, file is not formatted correctly\n");
  2176.     }
  2177.     
  2178.     return TRUE;
  2179. }
  2180.  
  2181.  
  2182.  
  2183. ////////////////////////////////////////////////////////////////////////
  2184. //
  2185. // Description:
  2186. //    This routine is called to get a file name using the
  2187. // standard file dialog.
  2188. //
  2189. // Use: private
  2190. void
  2191. SoSceneViewer::getFileName()
  2192. //
  2193. ////////////////////////////////////////////////////////////////////////
  2194. {
  2195.     // use a motif file selection dialog
  2196.     if (fileDialog == NULL) {
  2197.     Arg args[5];
  2198.     int n = 0;
  2199.     
  2200.     // unmanage when ok/cancel are pressed
  2201.     XtSetArg(args[n], XmNautoUnmanage, TRUE); n++;
  2202.     fileDialog = XmCreateFileSelectionDialog(
  2203.         XtParent(mgrWidget), "File Dialog", args, n);
  2204.     
  2205.     XtAddCallback(fileDialog, XmNokCallback,
  2206.               (XtCallbackProc)SoSceneViewer::fileDialogCB,
  2207.               (XtPointer)this);
  2208.     }
  2209.     
  2210.     // manage the dialog
  2211.     XtManageChild(fileDialog);
  2212. }
  2213.  
  2214. ////////////////////////////////////////////////////////////////////////
  2215. //
  2216. // Description:
  2217. //    Motif file dialog callback.
  2218. //
  2219. void
  2220. SoSceneViewer::fileDialogCB(Widget, SoSceneViewer *sv,
  2221.               XmFileSelectionBoxCallbackStruct *data)
  2222. //
  2223. ////////////////////////////////////////////////////////////////////////
  2224. {
  2225.     // Get the file name
  2226.     char *filename;
  2227.     XmStringGetLtoR(data->value,
  2228.     (XmStringCharSet) XmSTRING_DEFAULT_CHARSET, &filename);
  2229.     
  2230.     // Use that file
  2231.     sv->doFileIO(filename);
  2232.     
  2233.     XtFree(filename);
  2234. }
  2235.  
  2236. ////////////////////////////////////////////////////////////////////////
  2237. //
  2238. // Description:
  2239. //    detach everything and nuke the existing scene.
  2240. //
  2241. // Use: private
  2242. void
  2243. SoSceneViewer::deleteScene()
  2244. //
  2245. ////////////////////////////////////////////////////////////////////////
  2246. {
  2247.     // deselect everything (also detach manips)
  2248.     selection->deselectAll();
  2249.     
  2250.     // temporaly remove the light manips
  2251.     removeAttachedLightManipGeometry();
  2252.     
  2253.     // remove the geometry under the selection node
  2254.     for (int i = selection->getNumChildren(); i>0; i--)
  2255.     selection->removeChild(i-1);
  2256.     
  2257.     // add the light manips back in
  2258.     addAttachedLightManipGeometry();
  2259. }
  2260.  
  2261. ////////////////////////////////////////////////////////////////////////
  2262. //
  2263. // Description:
  2264. //    Read/Write to the given file name, given the current file mode.
  2265. //
  2266. // Use: private
  2267. void
  2268. SoSceneViewer::doFileIO(const char *file)
  2269. //
  2270. ////////////////////////////////////////////////////////////////////////
  2271. {
  2272.     SbBool okFile;
  2273.     
  2274.     switch (fileMode) {
  2275.     case SV_FILE_OPEN:
  2276.         deleteScene();
  2277.         okFile = readFile(file);
  2278.         break;
  2279.     case SV_FILE_IMPORT:
  2280.         okFile = readFile(file);
  2281.         break;
  2282.     case SV_FILE_SAVE_AS:
  2283.         okFile = writeFile(file);
  2284.         break;
  2285.     case SV_FILE_READ_ENV:
  2286.         readEnvFile(file);
  2287.         break;
  2288.     case SV_FILE_SAVE_ENV:
  2289.         // Manips new replace lights, so we have to make sure they
  2290.         // don't show up in the env file.
  2291.         // temporaly remove the light manips
  2292.         removeAttachedLightManipGeometry();
  2293.         
  2294.         writeEnvFile(file);
  2295.  
  2296.         // add the light manips back in
  2297.         addAttachedLightManipGeometry();
  2298.         break;
  2299.     default:
  2300.         fprintf(stderr, "Wrong file mode %d passed!\n", fileMode);
  2301.         return;
  2302.     }
  2303.     
  2304.     // save the new file name so we can simply use "Save" instead of
  2305.     // "Save As" next time around.
  2306.     if (fileMode == SV_FILE_OPEN || fileMode == SV_FILE_SAVE_AS) {
  2307.     
  2308.     // save the current file name
  2309.     delete fileName;
  2310.     if (okFile && file != NULL)
  2311.         fileName = strdup(file);
  2312.     else
  2313.         fileName = NULL;
  2314.     }
  2315.     
  2316.     // enable/disable cmd key shortcuts and menu items
  2317.     updateCommandAvailability();
  2318. }
  2319.  
  2320. ////////////////////////////////////////////////////////////////////////
  2321. //
  2322. // Description:
  2323. //    Saves the scene to the current file.
  2324. //
  2325. // Use: private
  2326. void
  2327. SoSceneViewer::save()
  2328. //
  2329. ////////////////////////////////////////////////////////////////////////
  2330. {
  2331.     if (fileName != NULL) {
  2332.     SbBool ok = writeFile(fileName);
  2333.     if (!ok) {
  2334.         delete fileName;
  2335.         fileName = NULL;
  2336.     }
  2337.     }
  2338.     else {
  2339.     fileMode = SV_FILE_SAVE_AS;
  2340.     getFileName();
  2341.     }
  2342. }
  2343.  
  2344. ////////////////////////////////////////////////////////////////////////
  2345. //
  2346. // Description:
  2347. //    Removes the attached light manips geometry from the scene. This
  2348. //  is used for file writting,...
  2349. //
  2350. // Use: private
  2351. void
  2352. SoSceneViewer::removeAttachedLightManipGeometry()
  2353. //
  2354. ////////////////////////////////////////////////////////////////////////
  2355. {
  2356.     for (int i = 0; i < lightDataList.getLength(); i++ ) {
  2357.     
  2358.     SvLightData *data = (SvLightData *) lightDataList[i];
  2359.  
  2360.     // We'll be putting everything back later, so make a note of this...
  2361.     data->shouldBeManip = data->isManip;
  2362.  
  2363.     if ( data->isManip == TRUE )
  2364.         editLight(data, FALSE);
  2365.     }
  2366. }
  2367.  
  2368. ////////////////////////////////////////////////////////////////////////
  2369. //
  2370. // Description:
  2371. //    Add the attached light manips geometry back into the scene. This
  2372. // is called after the geometry has been temporaly revomed (used for file
  2373. // writting).
  2374. //
  2375. // Use: private
  2376. void
  2377. SoSceneViewer::addAttachedLightManipGeometry()
  2378. //
  2379. ////////////////////////////////////////////////////////////////////////
  2380. {
  2381.     for (int i = 0; i < lightDataList.getLength(); i++ ) {
  2382.     
  2383.     SvLightData *data = (SvLightData *) lightDataList[i];
  2384.  
  2385.     if ( data->isManip != data->shouldBeManip )
  2386.         editLight(data, data->shouldBeManip);
  2387.     }
  2388. }
  2389.  
  2390. ////////////////////////////////////////////////////////////////////////
  2391. //
  2392. // Description:
  2393. //    Write the nodes under the selection node to the given file name.
  2394. //
  2395. // Use: private
  2396. SbBool
  2397. SoSceneViewer::writeFile(const char *filename)
  2398. //
  2399. ////////////////////////////////////////////////////////////////////////
  2400. {
  2401.     SoWriteAction   wa;
  2402.     
  2403.     if (! wa.getOutput()->openFile(filename)) {
  2404.     
  2405.     // display an error dialog
  2406.     char str[100];
  2407.     strcpy(str, "Error creating file: ");
  2408.     strcat(str, filename);
  2409.     SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str);
  2410.     
  2411.     return FALSE;
  2412.     }
  2413.     
  2414.     // temporarily replace all manips with regular transform nodes.
  2415.     removeManips();
  2416.  
  2417.     // Do the same for all the light manips
  2418.     removeAttachedLightManipGeometry();
  2419.     
  2420.     // write out all the children of the selection node
  2421.     for (int i = 0; i < selection->getNumChildren(); i++)
  2422.     wa.apply(selection->getChild(i));
  2423.     wa.getOutput()->closeFile();
  2424.     
  2425.     // Now put the manips back in the scene graph.
  2426.     restoreManips();
  2427.     
  2428.     // put the light manips back in the scene graph.
  2429.     addAttachedLightManipGeometry();
  2430.     
  2431.     return TRUE;
  2432. }
  2433.  
  2434. ////////////////////////////////////////////////////////////////////////
  2435. //
  2436. // Description:
  2437. //    Write the Enviroment nodes (camera and lights) to the given 
  2438. //  file name.
  2439. //
  2440. // Use: private
  2441. SbBool
  2442. SoSceneViewer::writeEnvFile(const char *filename)
  2443. //
  2444. ////////////////////////////////////////////////////////////////////////
  2445. {
  2446.     SoWriteAction    wa;
  2447.     
  2448.     if (! wa.getOutput()->openFile(filename)) {
  2449.     
  2450.     // display an error dialog
  2451.     char str[100];
  2452.     strcpy(str, "Error creating file: ");
  2453.     strcat(str, filename);
  2454.     SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str);
  2455.  
  2456.     return FALSE;
  2457.     }
  2458.     
  2459.     // write out the environment including the headlight
  2460.     wa.apply(envLabel);
  2461.     wa.apply(getCamera());
  2462.     wa.apply(environment);
  2463.     wa.apply(lightGroup);
  2464.     if (isHeadlight())
  2465.     wa.apply(getHeadlight());
  2466.     
  2467.     wa.getOutput()->closeFile();
  2468.     
  2469.     return TRUE;
  2470. }
  2471.  
  2472. ////////////////////////////////////////////////////////////////////////
  2473. //
  2474. // Description:
  2475. //    Print the scene using a custom print dialog.
  2476. //
  2477. // Use: private
  2478. void
  2479. SoSceneViewer::print()
  2480. //
  2481. ////////////////////////////////////////////////////////////////////////
  2482. {
  2483.     if (printDialog == NULL) {
  2484.         printDialog = new SoXtPrintDialog;
  2485.     printDialog->setTitle("SceneViewer Printing");
  2486.     printDialog->setBeforePrintCallback(
  2487.         SoSceneViewer::beforePrintCallback, (void *) this);
  2488.     printDialog->setAfterPrintCallback(
  2489.         SoSceneViewer::afterPrintCallback, (void *) this);
  2490.     }
  2491.     
  2492.     //
  2493.     // Send the render area size and scene graph to the print dialog
  2494.     //
  2495.     Widget widget = getRenderAreaWidget();
  2496.     if (widget != NULL) {
  2497.         Arg args[2];
  2498.         int n = 0;
  2499.         SbVec2s sz;
  2500.         XtSetArg(args[n], XtNwidth, &sz[0]); n++;
  2501.         XtSetArg(args[n], XtNheight, &sz[1]); n++;
  2502.         XtGetValues(widget, args, n);
  2503.         printDialog->setPrintSize(sz);
  2504.     }
  2505.     
  2506.     printDialog->show();
  2507. }
  2508.  
  2509. ////////////////////////////////////////////////////////////////////////
  2510. //
  2511. // Description:
  2512. //    Temporarily remove manips from the scene.
  2513. // Restore them with a call to restoreManips().
  2514. //
  2515. // Use: private
  2516. void
  2517. SoSceneViewer::removeManips()
  2518. //
  2519. ////////////////////////////////////////////////////////////////////////
  2520. {
  2521.     // temporarily replace all manips with regular transform nodes.
  2522.     for (int m = 0; m < maniplist->getLength(); m++ ) {
  2523.     SoTransformManip *manip = maniplist->getManip(m);    
  2524.     SoPath *xfPath = maniplist->getXfPath(m);
  2525.     manip->replaceManip(xfPath, NULL);
  2526.     }
  2527. }
  2528.  
  2529. ////////////////////////////////////////////////////////////////////////
  2530. //
  2531. // Description:
  2532. //    Restore manips that were removed with removeManips().
  2533. //
  2534. // Use: private
  2535. void
  2536. SoSceneViewer::restoreManips()
  2537. //
  2538. ////////////////////////////////////////////////////////////////////////
  2539. {
  2540.     // Now put the manips back in the scene graph.
  2541.     for (int m = 0; m < maniplist->getLength(); m++ ) {
  2542.     SoTransformManip *manip = maniplist->getManip(m);    
  2543.     SoPath *xfPath = maniplist->getXfPath(m);
  2544.     manip->replaceNode(xfPath);
  2545.     }
  2546. }
  2547.  
  2548. ////////////////////////////////////////////////////////////////////////
  2549. //
  2550. // Description:
  2551. //    Temporarily remove manips from the scene. They
  2552. // will all be restored after the printing is done.
  2553. //
  2554. // Use: private, static
  2555. void
  2556. SoSceneViewer::beforePrintCallback(void *uData, SoXtPrintDialog *)
  2557. //
  2558. ////////////////////////////////////////////////////////////////////////
  2559. {
  2560.     SoSceneViewer *sv = (SoSceneViewer *)uData;
  2561.     
  2562.     // temporarily replace all manips with regular transforms.
  2563.     sv->removeManips();
  2564.  
  2565.     // Do the same for all the light manips
  2566.     sv->removeAttachedLightManipGeometry();
  2567.     
  2568.     // if the current viewer is the examiner viewer, turn the 
  2569.     // feedback axis off while we print
  2570.     if (sv->whichViewer == SV_VWR_EXAMINER) {
  2571.     SoXtExaminerViewer *exam = (SoXtExaminerViewer *) sv->currentViewer;
  2572.     sv->feedbackShown = exam->isFeedbackVisible();
  2573.     exam->setFeedbackVisibility(FALSE);
  2574.     }
  2575.     
  2576.     // set the scene to print
  2577.     sv->printDialog->setSceneGraph(sv->sceneGraph);
  2578. }
  2579.  
  2580. ////////////////////////////////////////////////////////////////////////
  2581. //
  2582. // Description:
  2583. //    Called after printing is done. Add the manips back into the
  2584. // scene.
  2585. //
  2586. // Use: private, static
  2587. void
  2588. SoSceneViewer::afterPrintCallback(void *uData, SoXtPrintDialog *)
  2589. //
  2590. ////////////////////////////////////////////////////////////////////////
  2591. {
  2592.     SoSceneViewer *sv = (SoSceneViewer *)uData;
  2593.     
  2594.     // put the manips back in the scene graph.
  2595.     sv->restoreManips();
  2596.     
  2597.     // put the light manips back in the scene graph.
  2598.     sv->addAttachedLightManipGeometry();
  2599.     
  2600.     // restor the examiner feedback
  2601.     if (sv->whichViewer == SV_VWR_EXAMINER) {
  2602.     SoXtExaminerViewer *exam = (SoXtExaminerViewer *) sv->currentViewer;
  2603.     exam->setFeedbackVisibility( sv->feedbackShown );
  2604.     }
  2605. }
  2606.  
  2607. ////////////////////////////////////////////////////////////////////////
  2608. //
  2609. // Description:
  2610. //    Static routine for processing topbar menu events.
  2611. //  When the menu is created, it stores pointer to the SoSceneViewer
  2612. //  in the client_data, so that we can tell which SoSceneViewer needs
  2613. //  the event.
  2614. //
  2615. // Use: private, static
  2616. //
  2617. void
  2618. SoSceneViewer::processTopbarEvent(
  2619.     Widget,                // Which widget?  I don't care
  2620.     SoSceneViewerData *data,    // Pointer to button/SoSceneViewer
  2621.     XmAnyCallbackStruct *cb )    // X garbage
  2622. //
  2623. ////////////////////////////////////////////////////////////////////////
  2624. {
  2625.     SoSceneViewer *sv = data->classPt;
  2626.     Time eventTime = cb->event->xbutton.time;
  2627.     
  2628.     switch (data->id) {
  2629.     
  2630.     //
  2631.     // File
  2632.     //
  2633.     
  2634.     case SV_FILE_ABOUT:
  2635.     sv->showAboutDialog();
  2636.     break;
  2637.     
  2638.     case SV_FILE_NEW:
  2639.     sv->deleteScene();
  2640.     delete sv->fileName;
  2641.     sv->fileName = NULL;
  2642.     break;
  2643.     
  2644.     case SV_FILE_OPEN:
  2645.     case SV_FILE_IMPORT:
  2646.     case SV_FILE_SAVE_AS:
  2647.     case SV_FILE_READ_ENV:
  2648.     case SV_FILE_SAVE_ENV:
  2649.     sv->fileMode = data->id;
  2650.     sv->getFileName();
  2651.     break;
  2652.     
  2653.     case SV_FILE_SAVE:
  2654.     sv->save();
  2655.     break;
  2656.     
  2657.     case SV_FILE_PRINT:
  2658.         sv->print();
  2659.     break;
  2660.     case SV_FILE_QUIT:
  2661.     delete sv;
  2662.     exit(0);
  2663.     break;
  2664.     
  2665.     //
  2666.     // Edit
  2667.     //
  2668.     
  2669.     case SV_EDIT_PICK_PARENT:
  2670.     sv->pickParent();
  2671.     break;
  2672.     case SV_EDIT_PICK_ALL:
  2673.     sv->pickAll();
  2674.     break;
  2675.     case SV_EDIT_CUT:
  2676.     // Remove manipulators before cutting the selection.
  2677.     sv->detachManipFromAll();
  2678.     sv->clipboard->copy((SoPathList *)sv->selection->getList(), eventTime);
  2679.     sv->destroySelectedObjects();
  2680.     sv->updateCommandAvailability();
  2681.     break;
  2682.     case SV_EDIT_COPY:
  2683.     // Remove manipulators while copying the selection.
  2684.     sv->removeManips();
  2685.     sv->clipboard->copy((SoPathList *)sv->selection->getList(),  eventTime);
  2686.     sv->restoreManips();
  2687.     break;
  2688.     case SV_EDIT_PASTE:
  2689.     sv->clipboard->paste(eventTime, pasteDoneCB, sv);
  2690.     break;
  2691.     case SV_EDIT_DELETE:
  2692.     sv->destroySelectedObjects();
  2693.     sv->updateCommandAvailability();
  2694.     break;
  2695.     
  2696.     //
  2697.     // Viewing
  2698.     //
  2699.     
  2700.     case SV_VIEW_PICK:
  2701.     sv->setViewing(! sv->isViewing());
  2702.     break;
  2703. #ifdef EXPLORER
  2704.     case SV_VIEW_USER:
  2705.     sv->userModeFlag = !sv->userModeFlag;
  2706.     if (sv->userModeFlag)
  2707.         sv->currentViewer->setEventCallback(sv->userModeCB, sv->userModedata);
  2708.     else
  2709.         sv->currentViewer->setEventCallback(NULL, NULL);
  2710.     break;
  2711. #endif
  2712.     
  2713. // SPACEBALL
  2714. // Examiner viewer only for spaceball
  2715. #ifdef notdef
  2716.     case SV_VIEW_EXAMINER:
  2717.     sv->switchToViewer(SV_VWR_EXAMINER);
  2718.     break;
  2719.     case SV_VIEW_WALK:
  2720.     sv->switchToViewer(SV_VWR_WALK);
  2721.     break;
  2722.     case SV_VIEW_PLANE:
  2723.     sv->switchToViewer(SV_VWR_PLANE);
  2724.     break;
  2725.     case SV_VIEW_FLY:
  2726.     sv->switchToViewer(SV_VWR_FLY);
  2727.     break;
  2728. #endif
  2729.     
  2730.     case SV_VIEW_SELECTION:
  2731.         sv->viewSelection();
  2732.         break;
  2733.  
  2734.     case SV_VIEW_SCREEN_TRANSPARENCY:
  2735.     sv->setTransparencyType(SoGLRenderAction::SCREEN_DOOR);
  2736.     break;
  2737.     case SV_VIEW_BLEND_TRANSPARENCY:
  2738.     sv->setTransparencyType(SoGLRenderAction::BLEND);
  2739.     break;
  2740.     case SV_VIEW_DELAY_BLEND_TRANSPARENCY:
  2741.     sv->setTransparencyType(SoGLRenderAction::DELAYED_BLEND);
  2742.     break;
  2743.     case SV_VIEW_SORT_BLEND_TRANSPARENCY:
  2744.     sv->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_BLEND);
  2745.     break;
  2746.  
  2747.     case SV_VIEW_FOG:
  2748.         sv->setFog(! sv->fogFlag);
  2749.         break;
  2750.     case SV_VIEW_ANTIALIASING:
  2751.         sv->setAntialiasing(! sv->antialiasingFlag);
  2752.         break;
  2753.     case SV_VIEW_BKG_COLOR:
  2754.         sv->editBackgroundColor();
  2755.         break;
  2756.  
  2757. // SPACEBALL
  2758. // Pop up spaceball translation scale slider
  2759.     case SV_VIEW_SP_BALL_SCALE:
  2760.         sv->createTransformScaleEditor();
  2761.         break;
  2762.  
  2763.  
  2764.     //
  2765.     // Editors
  2766.     //
  2767.     case SV_EDITOR_MATERIAL:
  2768.     sv->createMaterialEditor();
  2769.     break;
  2770.  
  2771.     case SV_EDITOR_TRANSFORM:
  2772.     sv->createTransformSliderSet();
  2773.     break;
  2774.  
  2775.     case SV_EDITOR_COLOR:
  2776.     sv->createColorEditor();
  2777.     break;
  2778.  
  2779.     //
  2780.     // Selection
  2781.     //
  2782.     case SV_SEL_SINGLE_SELECT:
  2783.         sv->selection->policy.setValue(SoSelection::SINGLE);
  2784.     break;
  2785.  
  2786.     case SV_SEL_TOGGLE_SELECT:
  2787.         sv->selection->policy.setValue(SoSelection::TOGGLE);
  2788.     break;
  2789.  
  2790.     case SV_SEL_SHIFT_SELECT:
  2791.         sv->selection->policy.setValue(SoSelection::SHIFT);
  2792.     break;
  2793.  
  2794. #ifdef notdef
  2795.     case SV_TEXTURE_MAPPER:
  2796.         sv->editTexture();
  2797.         break;
  2798. #endif
  2799.  
  2800.  
  2801.     //
  2802.     // Manips
  2803.     //
  2804.     case SV_MANIP_TRACKBALL:
  2805.     sv->highlightRA->setVisible(FALSE); // highlight visible when no manip
  2806.     sv->curManip = (sv->curManip == SV_TRACKBALL) ? SV_NONE : SV_TRACKBALL;
  2807.     if ( sv->curManipReplaces )
  2808.         sv->replaceAllManips( sv->curManip );
  2809.         break;
  2810.  
  2811.     case SV_MANIP_HANDLEBOX:
  2812.     sv->highlightRA->setVisible(FALSE); // highlight visible when no manip
  2813.     sv->curManip = (sv->curManip == SV_HANDLEBOX) ? SV_NONE : SV_HANDLEBOX;
  2814.     if ( sv->curManipReplaces )
  2815.         sv->replaceAllManips( sv->curManip );
  2816.         break;
  2817.  
  2818.     case SV_MANIP_JACK:
  2819.     sv->highlightRA->setVisible(FALSE); // highlight visible when no manip
  2820.     sv->curManip = (sv->curManip == SV_JACK) ? SV_NONE : SV_JACK;
  2821.     if ( sv->curManipReplaces )
  2822.         sv->replaceAllManips( sv->curManip );
  2823.         break;
  2824.  
  2825.     case SV_MANIP_CENTERBALL:
  2826.     sv->highlightRA->setVisible(FALSE); // highlight visible when no manip
  2827.     sv->curManip =(sv->curManip == SV_CENTERBALL) ? SV_NONE : SV_CENTERBALL;
  2828.     if ( sv->curManipReplaces )
  2829.         sv->replaceAllManips( sv->curManip );
  2830.         break;
  2831.  
  2832.     case SV_MANIP_XFBOX:
  2833.     sv->highlightRA->setVisible(FALSE); // highlight visible when no manip
  2834.     sv->curManip = (sv->curManip == SV_XFBOX) ? SV_NONE : SV_XFBOX;
  2835.     if ( sv->curManipReplaces )
  2836.         sv->replaceAllManips( sv->curManip );
  2837.         break;
  2838.  
  2839.     case SV_MANIP_TABBOX:
  2840.     sv->highlightRA->setVisible(FALSE); // highlight visible when no manip
  2841.     sv->curManip = (sv->curManip == SV_TABBOX) ? SV_NONE : SV_TABBOX;
  2842.     if ( sv->curManipReplaces )
  2843.         sv->replaceAllManips( sv->curManip );
  2844.         break;
  2845.  
  2846.     case SV_MANIP_NONE:
  2847.     sv->highlightRA->setVisible(TRUE); // highlight visible when no manip
  2848.     sv->curManip = SV_NONE;
  2849.     if ( sv->curManipReplaces )
  2850.         sv->detachManipFromAll();
  2851.         break;
  2852.     case SV_MANIP_REPLACE_ALL:
  2853.     // Toggle the value of 'curManipReplaces'
  2854.     sv->curManipReplaces = ( sv->curManipReplaces == TRUE) ? FALSE : TRUE;
  2855.  
  2856.     if ( sv->curManipReplaces )
  2857.         sv->replaceAllManips( sv->curManip );
  2858.         break;
  2859.     
  2860.     //
  2861.     // Lights
  2862.     //
  2863.     case SV_LIGHT_AMBIENT_EDIT:    sv->editAmbientColor(); break;
  2864.     case SV_LIGHT_ADD_DIRECT:    sv->addLight(new SoDirectionalLight); break;
  2865.     case SV_LIGHT_ADD_POINT:    sv->addLight(new SoPointLight); break;
  2866.     case SV_LIGHT_ADD_SPOT:    
  2867.     {
  2868.         // Set the dropOffRate to be non-zero, or it will always work
  2869.         // like a point light.
  2870.         SoSpotLight *newSpot = new SoSpotLight;
  2871.         newSpot->dropOffRate = .01;
  2872.         sv->addLight(newSpot); 
  2873.     }
  2874.     break;
  2875.     
  2876.     case SV_LIGHT_TURN_ON:
  2877.     case SV_LIGHT_TURN_OFF:
  2878.     {
  2879.     SbBool onFlag = (data->id == SV_LIGHT_TURN_ON);
  2880.     for (int i=0; i < sv->lightDataList.getLength(); i++)
  2881.         sv->turnLightOnOff((SvLightData *) sv->lightDataList[i], onFlag);
  2882.     sv->turnLightOnOff( sv->headlightData, onFlag);
  2883.     }
  2884.     break;
  2885.     case SV_LIGHT_SHOW_ALL:
  2886.     case SV_LIGHT_HIDE_ALL:
  2887.     {
  2888.     SbBool onFlag = (data->id == SV_LIGHT_SHOW_ALL);
  2889.     for (int i=0; i < sv->lightDataList.getLength(); i++)
  2890.         sv->editLight((SvLightData *) sv->lightDataList[i], onFlag);
  2891.     }
  2892.     break;
  2893.     
  2894.     } // endswitch( topbar button )
  2895. }
  2896.  
  2897. ////////////////////////////////////////////////////////////////////////
  2898. //
  2899. // Description:
  2900. //    Adds the given light to the scene and to the menu.
  2901. //
  2902. // Use: private
  2903. //
  2904. void
  2905. SoSceneViewer::addLight(SoLight *light)
  2906. //
  2907. ////////////////////////////////////////////////////////////////////////
  2908. {
  2909.     // create the switch and light node and add it to the scene
  2910.     SoSwitch *lightSwitch = new SoSwitch;
  2911.     lightGroup->addChild(lightSwitch);
  2912.     lightSwitch->addChild(light);
  2913.     SWITCH_LIGHT_ON(lightSwitch);
  2914.     
  2915.     // add the light entry for the new light
  2916.     SvLightData *data = addLightEntry(light, lightSwitch);
  2917.     
  2918.     //
  2919.     // Try to come up with some meaningfull default position base
  2920.     // of the current camera view volume.
  2921.     //
  2922.     SoCamera *vwrCamera = getCamera(); // don't cache this in the class
  2923.     SbViewVolume vv = vwrCamera->getViewVolume(0.0);
  2924.     SbVec3f forward = - vv.zVector();
  2925.     SbVec3f center = vwrCamera->position.getValue() + forward * 
  2926.     (vwrCamera->nearDistance.getValue() + vwrCamera->farDistance.getValue()) / 2.0;
  2927.     SbVec3f position( vv.ulf + forward * vv.nearToFar * .25 );
  2928. //XXX ??? XXX
  2929. //XXX this algorithm should be replaced. Perhaps instead of using
  2930. //XXX 'forward' we could go a little up and to the left?
  2931. //XXX ??? XXX
  2932.         
  2933.     if (data->type == SoDirectionalLight::getClassTypeId()) {
  2934.     SoDirectionalLight *myLight = (SoDirectionalLight *) data->light;
  2935.     // the position of the light can't be given to the light itself.
  2936.     // So we use the translation and translation inverse to 
  2937.     // get it to go where we want.
  2938.     data->translation->translation = position;
  2939.     data->translationInverse->translation = -position;
  2940.     myLight->direction = center - position;
  2941.     }
  2942.     else {
  2943.     // The data->scale will influence the position we set.
  2944.     // So we need to prepare for this. Note, it's not a prolem for
  2945.     // directional lights since they use the translation node,
  2946.     // which is outside the scale and scaleInverse grouping
  2947.     SbVec3f invrs = data->scaleInverse->scaleFactor.getValue();
  2948.     SbVec3f scaledLoc = position;
  2949.     scaledLoc *= invrs[0]; 
  2950.  
  2951.     if (data->type == SoPointLight::getClassTypeId()) {
  2952.         SoPointLight *myLight = (SoPointLight *) data->light;
  2953.         myLight->location = scaledLoc;
  2954.         // no direction for this light
  2955.         }
  2956.         else if (data->type == SoSpotLight::getClassTypeId()) {
  2957.         SoSpotLight *myLight = (SoSpotLight *) data->light;
  2958.         myLight->location = scaledLoc;
  2959.         myLight->direction = center - position;
  2960.         }
  2961.     }
  2962. }
  2963.  
  2964. ////////////////////////////////////////////////////////////////////////
  2965. //
  2966. // Description:
  2967. //    Creates and append the light data struct, and adds a menu entry
  2968. //  for the light.
  2969. //
  2970. // Use: private
  2971. //
  2972. SvLightData *
  2973. SoSceneViewer::addLightEntry(SoLight *light, SoSwitch *lightSwitch)
  2974. //
  2975. ////////////////////////////////////////////////////////////////////////
  2976. {
  2977.     //
  2978.     // create the light data
  2979.     //
  2980.     
  2981.     SvLightData *data = new SvLightData;
  2982.     lightDataList.append(data);
  2983.  
  2984.     light->ref();
  2985.     data->light = light;
  2986.  
  2987.     data->lightSwitch = lightSwitch;
  2988.  
  2989.     // Try and find the scale, scaleInverse, translation, and 
  2990.     // translationInverse.
  2991.     data->scale = NULL;
  2992.     data->scaleInverse = NULL;
  2993.     data->translation = NULL;
  2994.     data->translationInverse = NULL;
  2995.     SbBool gotLight = FALSE;
  2996.     for ( int i = 0; i < lightSwitch->getNumChildren(); i++ ) {
  2997.     SoNode *n = lightSwitch->getChild(i);
  2998.     if (n == light)
  2999.         gotLight = TRUE;
  3000.     else if (n->isOfType(SoScale::getClassTypeId())){
  3001.         if (data->scale == NULL && gotLight == FALSE)
  3002.         data->scale = (SoScale *) n;
  3003.         else if (data->scaleInverse == NULL && gotLight == TRUE)
  3004.         data->scaleInverse = (SoScale *) n;
  3005.     }
  3006.     else if (n->isOfType(SoTranslation::getClassTypeId())){
  3007.         if (data->translation == NULL && gotLight == FALSE)
  3008.         data->translation = (SoTranslation *) n;
  3009.         else if (data->translationInverse == NULL && gotLight == TRUE)
  3010.         data->translationInverse = (SoTranslation *) n;
  3011.     }
  3012.     }
  3013.  
  3014.     // Now install any missing nodes...
  3015.     if (data->scale == NULL) {
  3016.     data->scale = new SoScale;
  3017.     int lightInd = lightSwitch->findChild(light);
  3018.         lightSwitch->insertChild( data->scale, lightInd );
  3019.     }
  3020.     if (data->scaleInverse == NULL) {
  3021.         data->scaleInverse = new SoScale;
  3022.     int lightInd = lightSwitch->findChild(light);
  3023.         lightSwitch->insertChild( data->scaleInverse, lightInd + 1 );
  3024.     }
  3025.     if (data->translation == NULL) {
  3026.     data->translation = new SoTranslation;
  3027.     int scaleInd = lightSwitch->findChild(data->scale);
  3028.         lightSwitch->insertChild( data->translation, scaleInd );
  3029.     }
  3030.     if (data->translationInverse == NULL) {
  3031.         data->translationInverse = new SoTranslation;
  3032.     int scaleInvInd = lightSwitch->findChild(data->scaleInverse);
  3033.         lightSwitch->insertChild( data->translationInverse, scaleInvInd+1 );
  3034.     }
  3035.     // See if the size was already calculated (this happens when we read 
  3036.     // .env files)...
  3037.     SbVec3f oldScale = data->scale->scaleFactor.getValue();
  3038.     if ( calculatedLightManipSize == FALSE 
  3039.      && oldScale != SbVec3f(1,1,1) ) {
  3040.     lightManipSize = oldScale[0];
  3041.     calculatedLightManipSize = TRUE;
  3042.     }
  3043.  
  3044.     data->classPt = this;
  3045.     data->colorEditor = NULL;
  3046.     data->isManip = FALSE;
  3047.     data->type = light->getTypeId();
  3048.     
  3049.     // set the correct label name
  3050.     char *str;
  3051.     if (data->type == SoDirectionalLight::getClassTypeId())
  3052.     str = "Directional ";
  3053.     else if (data->type == SoPointLight::getClassTypeId())
  3054.     str = "Point ";
  3055.     else if (data->type == SoSpotLight::getClassTypeId())
  3056.     str = "Spot ";
  3057.     else str = "??? ";
  3058.     data->name = strdup(str);
  3059.     
  3060.     //
  3061.     // by default attach the light manipulator to show the light
  3062.     //
  3063.     editLight(data, TRUE);
  3064.     
  3065.     //
  3066.     // add the menu entry
  3067.     //
  3068.     addLightMenuEntry(data);
  3069.     
  3070.     return data;
  3071. }
  3072.  
  3073. ////////////////////////////////////////////////////////////////////////
  3074. //
  3075. // Description:
  3076. //    build the light menu entry for the given light.
  3077. //
  3078. // Use: private
  3079. //
  3080. void
  3081. SoSceneViewer::addLightMenuEntry(SvLightData *data)
  3082. //
  3083. ////////////////////////////////////////////////////////////////////////
  3084. {
  3085.     //
  3086.     // create the motif menu entry
  3087.     //
  3088.     
  3089.     Widget menu = menuItems[SV_LIGHT].widget;
  3090.     
  3091.     // makes sure menu has been built
  3092.     if (menu == NULL)
  3093.     return;
  3094.     
  3095.     // create the submenu widget, adding a callback to update the toggles
  3096.     Arg args[8];
  3097.     int argnum = 0;
  3098. #ifdef MENUS_IN_POPUP
  3099.     SoXt::getPopupArgs(XtDisplay(menu), NULL, args, &argnum);
  3100. #endif
  3101.     data->submenuWidget = XmCreatePulldownMenu(menu, NULL, args, argnum);
  3102.     
  3103.     XtAddCallback(data->submenuWidget, XmNmapCallback,
  3104.     (XtCallbackProc) SoSceneViewer::lightSubmenuDisplay,
  3105.     (XtPointer) data);
  3106.     
  3107.     // create a cascade menu entry which will bring the submenu
  3108.     XtSetArg(args[0], XmNsubMenuId, data->submenuWidget);
  3109.     data->cascadeWidget = XtCreateWidget(data->name, 
  3110.     xmCascadeButtonGadgetClass, menu, args, 1);
  3111.     
  3112.     // add "on/off" toggle
  3113.     data->onOffWidget = XtCreateWidget("On/Off", xmToggleButtonGadgetClass, 
  3114.     data->submenuWidget, NULL, 0);
  3115.     XtAddCallback(data->onOffWidget, XmNvalueChangedCallback,
  3116.     (XtCallbackProc) SoSceneViewer::lightToggleCB, (XtPointer) data);
  3117.     
  3118.     // add "Icon" toggle
  3119.     data->iconWidget = XtCreateWidget("Icon", xmToggleButtonGadgetClass, 
  3120.     data->submenuWidget, NULL, 0);
  3121.     XtAddCallback(data->iconWidget, XmNvalueChangedCallback,
  3122.     (XtCallbackProc) SoSceneViewer::editLightToggleCB, (XtPointer) data);
  3123.     
  3124.     // add "Edit Color" toggle
  3125.     data->editColorWidget = XtCreateWidget("Edit Color", xmPushButtonGadgetClass, 
  3126.     data->submenuWidget, NULL, 0);
  3127.     XtAddCallback(data->editColorWidget, XmNactivateCallback,
  3128.     (XtCallbackProc) SoSceneViewer::editLightColorCB, (XtPointer) data);
  3129.     
  3130.     // add "Remove" entry
  3131.     data->removeWidget = XtCreateWidget("Remove", xmPushButtonGadgetClass, 
  3132.     data->submenuWidget, NULL, 0);
  3133.     XtAddCallback(data->removeWidget, XmNactivateCallback,
  3134.     (XtCallbackProc) SoSceneViewer::removeLightCB, (XtPointer) data);
  3135.     
  3136.     // manage children
  3137.     XtManageChild(data->onOffWidget);
  3138.     XtManageChild(data->iconWidget);
  3139.     XtManageChild(data->editColorWidget);
  3140.     XtManageChild(data->removeWidget);
  3141.     XtManageChild(data->cascadeWidget);
  3142. }
  3143.  
  3144. ////////////////////////////////////////////////////////////////////////
  3145. //
  3146. // Description:
  3147. //    Called by "On/Off" light menu entry when toggle changes.
  3148. //
  3149. // Use: static private
  3150. //
  3151. void
  3152. SoSceneViewer::lightToggleCB(Widget toggle, SvLightData *data, void *)
  3153. //
  3154. ////////////////////////////////////////////////////////////////////////
  3155. {
  3156.     data->classPt->turnLightOnOff(data, XmToggleButtonGetState(toggle));
  3157. }
  3158.  
  3159. ////////////////////////////////////////////////////////////////////////
  3160. //
  3161. // Description:
  3162. //    Turn the given light on or off.
  3163. //
  3164. // Use: private
  3165. //
  3166. void
  3167. SoSceneViewer::turnLightOnOff(SvLightData *data, SbBool flag)
  3168. //
  3169. ////////////////////////////////////////////////////////////////////////
  3170. {
  3171.     // check if it is the headlight
  3172.     if (data == headlightData)
  3173.     setHeadlight( flag );
  3174.     else {
  3175.     if ( flag )
  3176.         SWITCH_LIGHT_ON(data->lightSwitch);
  3177.     else
  3178.         SWITCH_LIGHT_OFF(data->lightSwitch);
  3179.     }
  3180. }
  3181.  
  3182. ////////////////////////////////////////////////////////////////////////
  3183. //
  3184. // Description:
  3185. //    "Edit" light menu entry callback.
  3186. //
  3187. // Use: static private
  3188. //
  3189. void
  3190. SoSceneViewer::editLightToggleCB(Widget toggle, SvLightData *data, void *)
  3191. //
  3192. ////////////////////////////////////////////////////////////////////////
  3193. {
  3194.     data->classPt->editLight( data, XmToggleButtonGetState(toggle) );
  3195. }
  3196.  
  3197. ////////////////////////////////////////////////////////////////////////
  3198. //
  3199. // Description:
  3200. //    Attach/detach the correct manipulator on the given light.
  3201. //
  3202. // Use: private
  3203. //
  3204. void
  3205. SoSceneViewer::editLight(SvLightData *data, SbBool flag)
  3206. //
  3207. ////////////////////////////////////////////////////////////////////////
  3208. {
  3209.     // ??? check if this is for the headlight, which is special cased
  3210.     // ??? since a manipulator cannot be used (aligned to camera).
  3211.     SbBool forHeadlight = (data == data->classPt->headlightData);
  3212.     
  3213.     //
  3214.     // attach the manip to the light and add it to the scene
  3215.     //
  3216.     if (flag) {
  3217.  
  3218.     if (forHeadlight) {
  3219.  
  3220.         if (headlightEditor == NULL) {
  3221.         headlightEditor = new SoXtDirectionalLightEditor;
  3222.         headlightEditor->setTitle("Headlight Editor");
  3223.         }
  3224.  
  3225.         // Make sure we have the current viewer's headlight
  3226.         SoLight *l = data->classPt->getHeadlight();
  3227.         l->ref();
  3228.         if (data->light)
  3229.         data->light->unref();
  3230.         data->light = l;
  3231.  
  3232.         // attach the dir light editor
  3233.         // ??? don't use the path from the root to the headlight 
  3234.         // ??? since we want the light to be relative to the 
  3235.         // ??? camera (i.e. moving the camera shouldn't affect
  3236.         // ??? the arrow in the editor since that direction
  3237.         // ??? is relative to the camera).
  3238.         SoPath *littlePath = new SoPath( data->light );
  3239.         headlightEditor->attach( littlePath );
  3240.         headlightEditor->show();
  3241.     }
  3242.     else if (data->isManip == FALSE) {
  3243.  
  3244.         // NOTE: if isManip == TRUE, then the light is already a manip
  3245.         // and doesn't need to be changed.
  3246.  
  3247.         SoLight *newManip = NULL;
  3248.     
  3249.         // allocate the right manipulator type if needed
  3250.         if (data->type == SoDirectionalLight::getClassTypeId()) {
  3251.         newManip = new SoDirectionalLightManip;
  3252.         newManip->ref();
  3253.         }
  3254.         else if (data->type == SoPointLight::getClassTypeId()) {
  3255.         newManip = new SoPointLightManip;
  3256.         newManip->ref();
  3257.         }
  3258.         else if (data->type == SoSpotLight::getClassTypeId()) {
  3259.         newManip = new SoSpotLightManip;
  3260.         newManip->ref();
  3261.             // Set dropOffRate non-zero, or it will look like a pointLight.
  3262.         ((SoSpotLightManip *)newManip)->dropOffRate = .01;
  3263.         }
  3264.  
  3265.         // get the path from the root to the light node
  3266.         SoSearchAction sa;
  3267.         sa.setNode( data->light );
  3268.         sa.apply( currentViewer->getSceneGraph() );
  3269.         SoPath *path = sa.getPath();
  3270.         // ??? light is probably turned off so we don't 
  3271.         // ??? need to print a warning message. Just don't
  3272.         // ??? do anything
  3273.         if (path == NULL) {
  3274.         newManip->unref();
  3275.         return;
  3276.         }
  3277.  
  3278.         path->ref();
  3279.         
  3280.  
  3281.         // Set the size for the manip. If this is the first one,  
  3282.         // then calculate a good size, based on the size of the scene.
  3283.         // Once this size is determined, use it for all other light manips.
  3284.         // (We need to save the value because the scene size will change
  3285.         //  over time, but we want all light manips to be the same size.
  3286.         
  3287.         if ( !calculatedLightManipSize ) {
  3288.         // Run a bounding box action on the scene...
  3289.         SoGetBoundingBoxAction ba(currentViewer->getViewportRegion());
  3290.         ba.apply( currentViewer->getSceneGraph() );
  3291.         SbBox3f sceneBox = ba.getBoundingBox();
  3292.         SbVec3f size = sceneBox.getMax() - sceneBox.getMin();
  3293. //XXX pick a good size!
  3294.         lightManipSize = .025 * size.length();
  3295.  
  3296.         calculatedLightManipSize = TRUE;
  3297.         }
  3298.         data->scale->scaleFactor.setValue( lightManipSize, lightManipSize, 
  3299.                            lightManipSize );
  3300.         float invSz = (lightManipSize == 0.0) ? 1.0 : 1.0 / lightManipSize;
  3301.         data->scaleInverse->scaleFactor.setValue( invSz, invSz, invSz );
  3302.  
  3303.         // Put the manip into the scene.
  3304.         if (data->type == SoPointLight::getClassTypeId())
  3305.         ((SoPointLightManip *)newManip)->replaceNode(path);
  3306.         else if (data->type == SoDirectionalLight::getClassTypeId())
  3307.         ((SoDirectionalLightManip *)newManip)->replaceNode(path);
  3308.         else if (data->type == SoSpotLight::getClassTypeId())
  3309.         ((SoSpotLightManip *)newManip)->replaceNode(path);
  3310.  
  3311.         // Okay, now that we stuck that manip in there,
  3312.         // we better make a note of it...
  3313.         path->unref();
  3314.         data->isManip = TRUE;
  3315.         data->light->unref();
  3316.         data->light = newManip;
  3317.  
  3318.     }
  3319.     }
  3320.     //
  3321.     // detach the manip from the light and remove it from the scene
  3322.     //
  3323.     else {
  3324.     if (forHeadlight) {
  3325.         // detach editor from light
  3326.         if (headlightEditor != NULL) {
  3327.         headlightEditor->detach();
  3328.         headlightEditor->hide();
  3329.         }
  3330.     }
  3331.     else if (data->isManip == TRUE ) {
  3332.         // replace the lightManip node with a regular light node
  3333.         // get the path from the root to the lightManip node
  3334.         SoSearchAction sa;
  3335.         sa.setNode( data->light );
  3336.         sa.apply( currentViewer->getSceneGraph() );
  3337.         SoPath *path = sa.getPath();
  3338.  
  3339.         if (path != NULL ) {
  3340.         path->ref();
  3341.         SoLight *newLight;
  3342.         if (data->type == SoPointLight::getClassTypeId()) {
  3343.             newLight = new SoPointLight;
  3344.             newLight->ref();
  3345.             ((SoPointLightManip *)data->light)->replaceManip(
  3346.                     path, (SoPointLight *)newLight );
  3347.         }
  3348.         else if (data->type == SoDirectionalLight::getClassTypeId()) {
  3349.             newLight = new SoDirectionalLight;
  3350.             newLight->ref();
  3351.             ((SoDirectionalLightManip *)data->light)->replaceManip(
  3352.                 path, (SoDirectionalLight *)newLight );
  3353.         }
  3354.         else if (data->type == SoSpotLight::getClassTypeId()) {
  3355.             newLight = new SoSpotLight;
  3356.             newLight->ref();
  3357.             ((SoSpotLightManip *)data->light)->replaceManip(
  3358.                     path, (SoSpotLight *)newLight );
  3359.         }
  3360.         path->unref();
  3361.         data->light->unref();
  3362.         data->light = newLight;
  3363.         data->isManip = FALSE;
  3364.         }
  3365.     }
  3366.     }
  3367. }
  3368.  
  3369. ////////////////////////////////////////////////////////////////////////
  3370. //
  3371. // Description:
  3372. //    Called by "Edit Color" light menu entry.
  3373. //
  3374. // Use: static private
  3375. //
  3376. void
  3377. SoSceneViewer::editLightColorCB(Widget, SvLightData *data, void *)
  3378. //
  3379. ////////////////////////////////////////////////////////////////////////
  3380. {
  3381.     // create the color editor with the right title
  3382.     if (data->colorEditor == NULL) {
  3383.     data->colorEditor = new MyColorEditor;
  3384.     char str[50];
  3385.     strcpy(str, data->name);
  3386.     strcat(str, " Light Color");
  3387.     data->colorEditor->setTitle(str);
  3388.     }
  3389.     
  3390.     if ( !data->colorEditor->isAttached() ) {
  3391.     // if this is for the headlight, make sure we have the
  3392.     // current viewer headlight
  3393.     if (data == data->classPt->headlightData) {
  3394.         SoLight *l = data->classPt->getHeadlight();
  3395.         l->ref();
  3396.         if (data->light)
  3397.         data->light->unref();
  3398.         data->light = l;
  3399.     }
  3400.     
  3401.     // normalize the light intensity
  3402.     SbColor col;
  3403.     col = data->light->color.getValue();
  3404.     col *= data->light->intensity.getValue();
  3405.     data->light->intensity.setValue( 1.0 );
  3406.     data->light->color.setValue( col );
  3407.     
  3408.     data->colorEditor->attach( &data->light->color, data->light );
  3409.     }
  3410.     
  3411.     data->colorEditor->show();
  3412. }
  3413.  
  3414. ////////////////////////////////////////////////////////////////////////
  3415. //
  3416. // Description:
  3417. //    remove button menu entry callback.
  3418. //
  3419. // Use: static private
  3420. //
  3421. void
  3422. SoSceneViewer::removeLightCB(Widget, SvLightData *data, void *)
  3423. //
  3424. ////////////////////////////////////////////////////////////////////////
  3425. {
  3426.     data->classPt->removeLight(data);
  3427. }
  3428.  
  3429. ////////////////////////////////////////////////////////////////////////
  3430. //
  3431. // Description:
  3432. //    removes the light from the scene, and removes the light data 
  3433. //  and pulldown menu entry.
  3434. //
  3435. // Use: private
  3436. //
  3437. void
  3438. SoSceneViewer::removeLight(SvLightData *data)
  3439. //
  3440. ////////////////////////////////////////////////////////////////////////
  3441. {
  3442.     // delete the color editor and manip
  3443.     delete data->colorEditor;
  3444.  
  3445. // note: deleted  code that dealt with the manip.
  3446. // Since the light and the manip are one and the same now.
  3447. // unrefing the light also removes the manip
  3448.  
  3449.     // unref the light (or manip) for this entry
  3450.     if (data->light)
  3451.     data->light->unref();
  3452.     
  3453.     // remove the light from the scene
  3454.     lightGroup->removeChild( data->lightSwitch );
  3455.     
  3456.     // nuke the menu entry
  3457.     if (data->cascadeWidget != NULL)
  3458.     XtDestroyWidget( data->cascadeWidget );
  3459.     
  3460.     // remove from list and delete the struct
  3461.     lightDataList.remove( lightDataList.find(data) );
  3462.     delete data;
  3463. }
  3464.  
  3465. ////////////////////////////////////////////////////////////////////////
  3466. //
  3467. // Description:
  3468. //    Called whenever a light submenu is mapped on screen (update 
  3469. //  the toggles)
  3470. //
  3471. // Use: static private
  3472. //
  3473. void
  3474. SoSceneViewer::lightSubmenuDisplay(Widget, SvLightData *data, void *)
  3475. //
  3476. ////////////////////////////////////////////////////////////////////////
  3477. {
  3478.     SoSceneViewer *sv = data->classPt;
  3479.     SbBool set;
  3480.     
  3481.     //
  3482.     // update the "on/off" toggle
  3483.     //
  3484.     if (data == sv->headlightData)
  3485.     set = sv->isHeadlight();
  3486.     else
  3487.     set = IS_LIGHT_ON(data->lightSwitch);
  3488.     if (set)
  3489.     TOGGLE_ON(data->onOffWidget);
  3490.     else
  3491.     TOGGLE_OFF(data->onOffWidget);
  3492.     
  3493.     //
  3494.     // update the "Edit" toggle
  3495.     //
  3496.     if (data == sv->headlightData)
  3497.     set = (sv->headlightEditor != NULL && sv->headlightEditor->isVisible());
  3498.     else
  3499.     set = (data->isManip == TRUE );
  3500.     if (set)
  3501.     TOGGLE_ON(data->iconWidget);
  3502.     else
  3503.     TOGGLE_OFF(data->iconWidget);
  3504. }
  3505.  
  3506. ////////////////////////////////////////////////////////////////////////
  3507. //
  3508. // Description:
  3509. //    Called after a paste operation has completed.
  3510. //
  3511. // Use: static, private
  3512. //
  3513. void
  3514. SoSceneViewer::pasteDoneCB(void *userData, SoPathList *pathList)
  3515. //
  3516. ////////////////////////////////////////////////////////////////////////
  3517. {
  3518.     SoSceneViewer *sv = (SoSceneViewer *) userData;
  3519.     sv->pasteDone(pathList);
  3520. }
  3521.  
  3522. ////////////////////////////////////////////////////////////////////////
  3523. //
  3524. // Description:
  3525. //    Called after a paste operation has completed, this adds the
  3526. // pasted data to our scene graph.
  3527. //
  3528. // Use: private
  3529. //
  3530. void
  3531. SoSceneViewer::pasteDone(SoPathList *pathList)
  3532. //
  3533. ////////////////////////////////////////////////////////////////////////
  3534. {
  3535.     if (pathList->getLength() <= 0)
  3536.         return;
  3537.  
  3538.     // first, detach manips from all selected objects
  3539.     detachManipFromAll();
  3540.     
  3541.     // now, turn off the sel/desel callbacks.
  3542.     // we'll turn them on again after we've adjusted the selection
  3543.     selection->removeSelectionCallback( SoSceneViewer::selectionCallback, this );
  3544.     selection->removeDeselectionCallback( SoSceneViewer::deselectionCallback, this );
  3545.     
  3546.     // now deselect all, and build up a selection from the pasted paths
  3547.     selection->deselectAll();
  3548.     
  3549.     // Add every path in the path list as a child under selection.
  3550.     // Then select each of these paths.
  3551.     for (int i = 0; i < pathList->getLength(); i++) {
  3552.     
  3553.         // if the head of the path is a selection node, then don't
  3554.     // paste the head - rather, paste all of its children. 
  3555.     // this makes sure we don't have more than 1 selection node.
  3556.     // While we're adding the paths as children, select each path.
  3557.         SoPath *p = (*pathList)[i];
  3558.     SoNode *head = p->getHead();
  3559.     SoPath *selpath;
  3560.     if (head->isOfType(SoSelection::getClassTypeId())) {
  3561.         for (int j = 0; j < ((SoSelection *)head)->getNumChildren(); j++) {
  3562.             selection->addChild(((SoSelection *)head)->getChild(j));
  3563.         
  3564.         // create a path from selection to this child
  3565.         // and select the path.
  3566.         selpath = new SoPath(selection);
  3567.         selpath->append(selection->getNumChildren() - 1);
  3568.         selection->select(selpath);
  3569.         }
  3570.     }
  3571.     else {
  3572.         // not a selection node, so just add it.
  3573.         selection->addChild(p->getHead());
  3574.         
  3575.         // create a path from selection to this child
  3576.         // and select the path.
  3577.         selpath = new SoPath(selection);
  3578.         selpath->append(selection->getNumChildren() - 1);
  3579.         selection->select(selpath);
  3580.     }
  3581.     }
  3582.     
  3583.     // now add manips to all the selected objects
  3584.     attachManipToAll(curManip);
  3585.     
  3586.     // and turn the sel/desel callbacks back on
  3587.     selection->addSelectionCallback( SoSceneViewer::selectionCallback, this );
  3588.     selection->addDeselectionCallback( SoSceneViewer::deselectionCallback, this );
  3589.     
  3590.     // enable/disable keyboard shortcuts
  3591.     updateCommandAvailability();
  3592.  
  3593.     delete pathList;
  3594. }
  3595.  
  3596. ////////////////////////////////////////////////////////////////////////
  3597. //
  3598. // Description:
  3599. //    Build routine for SceneViewer.  This creates all of the X widgets
  3600. //
  3601. // Use: public, virtual
  3602.  
  3603. Widget
  3604. SoSceneViewer::buildWidget(Widget parent)
  3605. //
  3606. ////////////////////////////////////////////////////////////////////////
  3607. {
  3608.     int        n;
  3609.     Arg        args[10];
  3610.     
  3611.     // create a form to hold everything together
  3612.     SbVec2s size = getSize();
  3613.     n = 0;
  3614.     if (size[0] != 0 && size[1] != 0) {
  3615.     XtSetArg(args[n], XtNwidth, size[0]); n++;
  3616.     XtSetArg(args[n], XtNheight, size[1]); n++;
  3617.     }
  3618.     mgrWidget = XtCreateWidget(getWidgetName(), xmFormWidgetClass, parent, args, n);
  3619.     registerWidget(mgrWidget);
  3620.     
  3621.     // create the topbar menu
  3622.     if (showMenuFlag)
  3623.     buildAndLayoutMenu(mgrWidget);
  3624.     
  3625.     // build and layout the current viewer
  3626.     whichViewer = SV_VWR_EXAMINER;
  3627.     setTitle("SceneViewer (Examiner)");
  3628.     currentViewer = viewerList[whichViewer] = new SoXtExaminerViewer(mgrWidget);
  3629.     currentViewer->setSceneGraph(sceneGraph);
  3630.     currentViewer->setGLRenderAction(highlightRA);
  3631.     currentViewer->redrawOnSelectionChange(selection);
  3632.     
  3633.     //setOverlayLogo(currentViewer);
  3634.     
  3635.     // Fog
  3636.     environment->fogColor.setValue( currentViewer->getBackgroundColor() );
  3637.  
  3638.     // since we created the camera, do a view all and save this
  3639.     // as the starting point (don't want default camera values).
  3640.     viewAll();
  3641.     saveHomePosition();
  3642.  
  3643.  
  3644.     buildAndLayoutViewer(currentViewer);
  3645.     // SPACEBALL
  3646.     // Set up the space ball
  3647.     setupSpaceball();
  3648.     
  3649.  
  3650.     
  3651.     // manage those children
  3652.     if (showMenuFlag)
  3653.     XtManageChild(menuWidget);
  3654.     currentViewer->show();
  3655.  
  3656.     // clipboard is for copy/paste of 3d data.
  3657.     //??? what if this SceneViewer had its widget destroyed and rebuilt?
  3658.     //??? we need to destroy the clipboards when that happens.
  3659.     clipboard = new SoXtClipboard(mgrWidget);
  3660.     
  3661.     return mgrWidget;
  3662. }
  3663.  
  3664. ////////////////////////////////////////////////////////////////////////
  3665. // SPACEBALL
  3666. // Description:
  3667. //      Setup routine for the spaceball.  Does all necessary initialization
  3668. //      for the spaceball if it is present.
  3669. //
  3670. // Use: private
  3671. void
  3672. SoSceneViewer::setupSpaceball() {
  3673. //
  3674. ////////////////////////////////////////////////////////////////////////
  3675.  
  3676.     // If the spaceball exists, then set set up all the stuff we need for it
  3677.     if ( SoXtSpaceball::exists()) {
  3678.         // add spaceball device
  3679.         if (spaceball == NULL) {
  3680.             spaceball = new SoXtSpaceball;
  3681.             // transformScaleFactor dictates the speed at which we will
  3682.             // translate.
  3683.             transformScaleFactor = .5;
  3684.         }
  3685.  
  3686.         // If the scene has been read in, find out what its size is, then
  3687.         // use that size to set the scale factor for spaceball translation
  3688.         SoGetBoundingBoxAction  *sceneBBoxAction =
  3689.                     new SoGetBoundingBoxAction(
  3690.                     currentViewer->getViewportRegion());
  3691.         if (selection->getNumChildren() != 0) {
  3692.             // This section of code determines the size of the scene
  3693.             // being viewed and uses that to determine how to set the
  3694.             // TranslationScaleFactor for spaceball events.
  3695.             sceneBBoxAction->apply(selection->getChild(0));
  3696.             SbBox3f sceneBBox = sceneBBoxAction->getBoundingBox();
  3697.             SbVec3f min = sceneBBox.getMin();
  3698.             SbVec3f max = sceneBBox.getMax();
  3699.             average = (fabsf(max[0] - min[0]) + fabsf(max[1]-min[1]) +
  3700.                         fabsf(max[2]-min[2])) / 3;
  3701.             spaceball->setTranslationScaleFactor(
  3702.                         (transformScaleFactor*average)/10000);
  3703.         }
  3704.  
  3705.         // Let the viewer know about the spaceball, turn decoration and
  3706.         // viewing off, so that the spaceball can do the driving
  3707.         currentViewer->registerDevice(spaceball);
  3708.         currentViewer->setDecoration(FALSE);
  3709.         currentViewer->setViewing(FALSE);
  3710.  
  3711.         if (selection->getNumChildren() != 0) {
  3712.             // Set up a callback for motion and button events 
  3713.             // from the spaceball, but only when there is already a scene
  3714.             SoEventCallback *cbNode = new SoEventCallback;
  3715.             sceneGraph->addChild(cbNode);
  3716.             cbNode->addEventCallback(SoMotion3Event::getClassTypeId(), 
  3717.                 my3DeventCB, this);
  3718.             cbNode->addEventCallback(SoSpaceballButtonEvent::getClassTypeId(),
  3719.                 my3DeventCB, this);
  3720.  
  3721.             // Now find the transform node to attach the spaceball movement
  3722.             // to
  3723.             SoPath *pathToTransform = new SoPath(selection->getChild(0));
  3724.             SoPath      *editTransformPath =
  3725.                 findTransformForAttach(pathToTransform);
  3726.             xform = (SoTransform *) ((SoFullPath *)
  3727.                 editTransformPath)->getTail();
  3728.         } else {
  3729.             xform = NULL;
  3730.         }
  3731.     }
  3732. }
  3733.  
  3734.  
  3735.  
  3736.  
  3737. ////////////////////////////////////////////////////////////////////////
  3738. // SPACEBALL
  3739. // Description:
  3740. //      Spaceball event handling routine.  Called whenever the spaceball
  3741. //    is touched.
  3742. //      
  3743. //
  3744. // Use: private, static
  3745. void
  3746. SoSceneViewer::my3DeventCB(void *userData, SoEventCallback *cb)
  3747. //
  3748. ////////////////////////////////////////////////////////////////////////
  3749. {
  3750.     // Get the Inventor event and a pointer to this class which was
  3751.     // passed in userData.
  3752.     const SoEvent *event = cb->getEvent();
  3753.     SoSceneViewer *sv = (SoSceneViewer *) userData;
  3754.  
  3755.     SoPath      *editTransformPath;
  3756.     // If the event really is a SoMotion3Event, do something with it,
  3757.     // depending on what particular event it is.
  3758.     if (event->isOfType(SoMotion3Event::getClassTypeId())) {
  3759.         const SoMotion3Event *motion = (const SoMotion3Event *) event;
  3760.         // If nothing is selected and we have not set up a transform node
  3761.     // to effect with the spaceball, then go search out the proper
  3762.         // transform node.
  3763.         if ( (sv->selection->getNumSelected() == 0) && sv->xform == NULL) {
  3764.             SoPath      *pathToTransform = 
  3765.                 new SoPath(sv->selection->getChild(0));
  3766.             SoPath      *editTransformPath =
  3767.                 sv->findTransformForAttach(pathToTransform);
  3768.             sv->xform = (SoTransform *) ((SoFullPath *)
  3769.                 editTransformPath)->getTail();
  3770.         }
  3771.  
  3772.         // Now do any transformations based on spaceball movement.
  3773.         sv->xform->translation.setValue(
  3774.             sv->xform->translation.getValue() + 
  3775.              motion->getTranslation().getValue());
  3776.  
  3777.         sv->xform->rotation.setValue(
  3778.             sv->xform->rotation.getValue() * motion->getRotation().getValue());
  3779.     // If the spaceball button is pressed, reset to home position.
  3780.     } else if (SO_SPACEBALL_PRESS_EVENT(event, PICK)) {
  3781.         // reset!
  3782.         sv->xform->rotation.setValue(0, 0, 0, 1);
  3783.         sv->xform->translation.setValue(0, 0, 0);
  3784.         sv->viewAll();
  3785.     }
  3786. }
  3787.  
  3788.  
  3789. ////////////////////////////////////////////////////////////////////////
  3790. //
  3791. // Description:
  3792. //    Builds and layout the given viewer.
  3793. //
  3794. // Use: private
  3795. void
  3796. SoSceneViewer::buildAndLayoutViewer(SoXtFullViewer *vwr)
  3797. //
  3798. ////////////////////////////////////////////////////////////////////////
  3799. {
  3800.     if (mgrWidget == NULL)
  3801.     return;
  3802.     
  3803.     // build the viewer if necessary
  3804.     if (vwr->getWidget() == NULL) {
  3805.     fprintf(stderr, "ERROR - need to create the viewer widget before it can be layed out\n");
  3806.     return;
  3807.     }
  3808.     
  3809.     // layout the viewer to be attached under the topbar menu
  3810.     // (if the pulldown menu is shown)
  3811.     Arg args[12];
  3812.     int n = 0;
  3813.     if ( showMenuFlag ) {
  3814.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_WIDGET); n++;
  3815.     XtSetArg(args[n], XmNtopWidget,        menuWidget); n++;
  3816.     }
  3817.     else {
  3818.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM); n++;
  3819.     }
  3820.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM); n++;
  3821.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM); n++;
  3822.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM); n++;
  3823.     XtSetValues(vwr->getWidget(), args, n);
  3824.  
  3825. }
  3826.  
  3827. ////////////////////////////////////////////////////////////////////////
  3828. //
  3829. // After realization, we can set up the color map for the popup menu windows.
  3830. //
  3831. // Use: protected
  3832. //
  3833. void
  3834. SoSceneViewer::afterRealizeHook()
  3835. //
  3836. ////////////////////////////////////////////////////////////////////////
  3837. {
  3838.     SoXtComponent::afterRealizeHook();
  3839.     
  3840. #ifdef MENUS_IN_POPUP
  3841.     if (popupWidget)
  3842.     SoXt::addColormapToShell(popupWidget, SoXt::getShellWidget(getWidget()));
  3843. #endif
  3844. }
  3845.  
  3846. ////////////////////////////////////////////////////////////////////////
  3847. //
  3848. // Description:
  3849. //    Create topbar menu.  Invalid buttons are rendered gray.
  3850. //      Each button's callback include a structure with the ID
  3851. //      of the button and a pointer to the SoSceneViewer that created
  3852. //      it.
  3853. //
  3854. // Use: private
  3855. //
  3856. void
  3857. SoSceneViewer::buildAndLayoutMenu(Widget parent)
  3858. //
  3859. ////////////////////////////////////////////////////////////////////////
  3860. {
  3861.     if (menuWidget != NULL)
  3862.     return;
  3863.     
  3864.     Arg            args[8];
  3865.     int            i, j, n, id;
  3866.     WidgetList        buttons, subButtons;
  3867.     int            itemCount, subItemCount;
  3868.     WidgetClass            widgetClass;
  3869.     String              callbackReason;
  3870.     XmString        xmstr;
  3871.     
  3872.     // create topbar menu
  3873.     menuWidget = XmCreateMenuBar(parent, "menuBar", NULL, 0);
  3874.  
  3875.     Arg popupargs[4];
  3876.     int popupn = 0;
  3877. #ifdef MENUS_IN_POPUP
  3878.     SoXt::getPopupArgs(XtDisplay(menuWidget), NULL, popupargs, &popupn);
  3879. #endif
  3880.  
  3881.     itemCount = XtNumber(pulldownData);
  3882.     buttons = (WidgetList) XtMalloc(itemCount * sizeof(Widget));
  3883.  
  3884.     for (i = 0; i < itemCount; i++) {
  3885.     // Make Topbar menu button
  3886.     Widget subMenu = XmCreatePulldownMenu(menuWidget, NULL, popupargs, popupn);
  3887.     // we only need one widget for loading the proper popup colormap
  3888.     if (! popupWidget)
  3889.         popupWidget = subMenu;
  3890.     
  3891.     id = pulldownData[i].id;
  3892.     menuItems[id].widget = subMenu;
  3893.         XtAddCallback(subMenu, XmNmapCallback,
  3894.         (XtCallbackProc) SoSceneViewer::menuDisplay,
  3895.         (XtPointer) &menuItems[id]);
  3896.  
  3897.         XtSetArg(args[0], XmNsubMenuId, subMenu);
  3898.     buttons[i] = XtCreateWidget(pulldownData[i].name,
  3899.         xmCascadeButtonGadgetClass, menuWidget, args, 1);
  3900.  
  3901.     // Make subMenu buttons
  3902.     subItemCount = pulldownData[i].subItemCount;
  3903.     subButtons = (WidgetList) XtMalloc(subItemCount * sizeof(Widget));
  3904.     
  3905.     for (j = 0; j < subItemCount; j++) {
  3906.         if (pulldownData[i].subMenu[j].id == SV_SEPARATOR)
  3907.         subButtons[j] = XtCreateWidget(NULL, xmSeparatorGadgetClass, 
  3908.             subMenu, NULL, 0);
  3909.         else {
  3910.                 switch (pulldownData[i].subMenu[j].buttonType) {
  3911.             case SV_PUSH_BUTTON:
  3912.             widgetClass = xmPushButtonGadgetClass;
  3913.             callbackReason = XmNactivateCallback;
  3914.             n = 0;
  3915.                 break;
  3916.             case SV_TOGGLE_BUTTON:
  3917.             widgetClass = xmToggleButtonGadgetClass;
  3918.             callbackReason = XmNvalueChangedCallback;
  3919.             n = 0;
  3920.                 break;
  3921.             case SV_RADIO_BUTTON:
  3922.             widgetClass = xmToggleButtonGadgetClass;
  3923.             callbackReason = XmNvalueChangedCallback;
  3924.             XtSetArg(args[0], XmNindicatorType, XmONE_OF_MANY);
  3925.             n = 1;
  3926.                 break;
  3927.             default:
  3928.             fprintf(stderr, "SceneViewer INTERNAL ERROR: bad buttonType\n");
  3929.                 break;
  3930.         }
  3931.         
  3932.         // check for keyboard accelerator
  3933.         char *accel = pulldownData[i].subMenu[j].accelerator;
  3934.         char *accelText = pulldownData[i].subMenu[j].accelText;
  3935.         xmstr = NULL;
  3936.         if (accel != NULL) {
  3937.             XtSetArg(args[n], XmNaccelerator, accel); n++;
  3938.             
  3939.             if (accelText != NULL) {
  3940.             xmstr = XmStringCreate(accelText,
  3941.                      XmSTRING_DEFAULT_CHARSET);
  3942.             XtSetArg(args[n], XmNacceleratorText, xmstr); n++;
  3943.             }
  3944.         }
  3945.         
  3946.         subButtons[j] = XtCreateWidget(
  3947.             pulldownData[i].subMenu[j].name,
  3948.             widgetClass,
  3949.             subMenu, args, n);
  3950.         
  3951.         if (xmstr != NULL)
  3952.             XmStringFree(xmstr);
  3953.         id = pulldownData[i].subMenu[j].id;
  3954.         menuItems[id].widget = subButtons[j];
  3955.         XtAddCallback(subButtons[j], callbackReason,
  3956.             (XtCallbackProc)SoSceneViewer::processTopbarEvent,
  3957.             (XtPointer) &menuItems[id]);
  3958.         }
  3959.     }
  3960.     XtManageChildren(subButtons, subItemCount);
  3961.     XtFree((char *)subButtons);
  3962.     }
  3963.     XtManageChildren(buttons, itemCount);
  3964.     XtFree((char *)buttons);
  3965.     
  3966.     //
  3967.     // layout the menu bar
  3968.     //
  3969.     n = 0;
  3970.     XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
  3971.     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
  3972.     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
  3973.     XtSetValues(menuWidget, args, n);
  3974.     
  3975.     //
  3976.     // Add the light items which are dynamically created
  3977.     //    
  3978.     
  3979.     // first add the headlight
  3980.     addLightMenuEntry(headlightData);
  3981.     XtUnmanageChild(headlightData->removeWidget);
  3982.     XtUnmanageChild(headlightData->editColorWidget);
  3983.     xmstr = XmStringCreate("Edit", XmSTRING_DEFAULT_CHARSET);
  3984.     XtSetArg(args[0], XmNlabelString, xmstr);
  3985.     XtSetValues(headlightData->iconWidget, args, 1);
  3986.     XmStringFree(xmstr);
  3987.     
  3988.     // now the regular lights
  3989.     for (i=0; i < lightDataList.getLength(); i++)
  3990.     addLightMenuEntry( (SvLightData *) lightDataList[i] );
  3991. }
  3992.  
  3993. ////////////////////////////////////////////////////////////////////////
  3994. //
  3995. // Description:
  3996. //    Show/hide the pulldown menu bar.
  3997. //
  3998. // Use: public
  3999. void
  4000. SoSceneViewer::showMenu(SbBool flag)
  4001. //
  4002. ////////////////////////////////////////////////////////////////////////
  4003. {
  4004.     if (showMenuFlag == flag || mgrWidget == NULL) {
  4005.     showMenuFlag = flag;
  4006.     return;
  4007.     }
  4008.     
  4009.     showMenuFlag = flag;
  4010.     
  4011.     if ( showMenuFlag ) {
  4012.     
  4013.     // turn topbar menu on
  4014.     if (menuWidget == NULL)
  4015.         buildAndLayoutMenu(mgrWidget);
  4016.     XtManageChild(menuWidget);
  4017.         
  4018.     // attach viewer to bottom of menu
  4019.     Arg args[2];
  4020.     int n = 0;
  4021.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_WIDGET); n++;
  4022.     XtSetArg(args[n], XmNtopWidget,        menuWidget);    n++;
  4023.     XtSetValues(currentViewer->getWidget(), args, n);
  4024.     }
  4025.     else {
  4026.     // attach viewer to form
  4027.     Arg args[2];
  4028.     int n = 0;
  4029.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM); n++;
  4030.     XtSetValues(currentViewer->getWidget(), args, n);
  4031.         
  4032.     // turn topbar menu off
  4033.     if (menuWidget != NULL)
  4034.         XtUnmanageChild(menuWidget);
  4035.     }
  4036. }
  4037.  
  4038. ////////////////////////////////////////////////////////////////////////
  4039. //
  4040. //  Manage the changes in the selected node(s)
  4041. //
  4042. //  Use: private, static
  4043. //
  4044. SoPath *
  4045. SoSceneViewer::pickFilterCB(void *userData, const SoPickedPoint *pick)
  4046. //
  4047. ////////////////////////////////////////////////////////////////////////
  4048. {
  4049.     SoSceneViewer *sv = (SoSceneViewer *) userData;
  4050.     SoPath *filteredPath = NULL;
  4051.     
  4052.     // If there are any transform manips along the path, check if they
  4053.     // belong to our personal set of manips. 
  4054.     // If so, change the path so it points to the object the manip
  4055.     // is attached to.
  4056.     SoFullPath *fullP = (SoFullPath *) pick->getPath();
  4057.     SoNode     *n;
  4058.     for (int i = 0; i < fullP->getLength(); i++ ) {
  4059.     n = fullP->getNode(i);
  4060.     if (n->isOfType(SoTransformManip::getClassTypeId())) {
  4061.         int which = sv->maniplist->find((SoTransformManip *) n);
  4062.         if (which != -1) {
  4063.         filteredPath = sv->maniplist->getSelectionPath(which);
  4064.         return filteredPath;
  4065.         }
  4066.     }
  4067.     }
  4068.  
  4069.     // If we didn't pick one of our manipulators, then return the pickPath
  4070.     filteredPath = pick->getPath();
  4071.     return filteredPath;
  4072. }
  4073.  
  4074. ////////////////////////////////////////////////////////////////////////
  4075. //
  4076. //  Manage the changes in the selected node(s)
  4077. //
  4078. //  Use: private, static
  4079. //
  4080. void
  4081. SoSceneViewer::deselectionCallback( void *userData,                // my data
  4082.                     SoPath *deselectedPath ) // object
  4083. //
  4084. ////////////////////////////////////////////////////////////////////////
  4085. {
  4086.     SoSceneViewer *sv = (SoSceneViewer *) userData;
  4087.  
  4088.     // remove the manip
  4089.     sv->detachManip( deselectedPath );
  4090.     
  4091.     // Remove editors
  4092.     if (sv->materialEditor)
  4093.     sv->materialEditor->detach();
  4094.     
  4095.     if (sv->colorEditor)
  4096.         sv->colorEditor->detach();
  4097.  
  4098.     if (sv->transformSliderSet)
  4099.     sv->transformSliderSet->setNode( NULL );
  4100.  
  4101.     // enable/disable cmd key shortcuts and menu items
  4102.     sv->updateCommandAvailability();
  4103. }
  4104.  
  4105. ////////////////////////////////////////////////////////////////////////
  4106. //
  4107. //  Manage the changes in the selected node(s)
  4108. //
  4109. //  Use: private, static
  4110. //
  4111. void
  4112. SoSceneViewer::selectionCallback( void *userData,         // my data
  4113.                   SoPath *selectedPath ) // object
  4114. //
  4115. ////////////////////////////////////////////////////////////////////////
  4116. {
  4117.     SoSceneViewer *sv = (SoSceneViewer *) userData;
  4118.  
  4119.     // attach the manip
  4120.     sv->attachManip( sv->curManip, selectedPath );
  4121.  
  4122.     //
  4123.     // If active, attach editors to new selection.
  4124.     //
  4125.     SoMaterial *mtl = NULL;
  4126.     if ( sv->materialEditor && sv->materialEditor->isVisible()) {
  4127.     mtl = sv->findMaterialForAttach(selectedPath);
  4128.     sv->materialEditor->attach(mtl);
  4129.     }
  4130.     
  4131.     if (sv->colorEditor && sv->colorEditor->isVisible()) {
  4132.         if (mtl == NULL)
  4133.         mtl = sv->findMaterialForAttach(selectedPath);
  4134.         sv->colorEditor->attach(&(mtl->diffuseColor), 0, mtl);
  4135.     }
  4136.     
  4137.     if ( sv->transformSliderSet && sv->transformSliderSet->isVisible() ) {
  4138.     SoPath      *editTransformPath;
  4139.     editTransformPath = sv->findTransformForAttach( selectedPath );
  4140.         sv->xform = (SoTransform *) ((SoFullPath *)
  4141.                 editTransformPath)->getTail();
  4142.     if ( editTransformPath == NULL ) {
  4143.         sv->transformSliderSet->setNode( NULL );
  4144.     }
  4145.     else  {
  4146.         editTransformPath->ref();
  4147.         sv->transformSliderSet->setNode(((SoFullPath *)editTransformPath)->getTail() );
  4148.         editTransformPath->unref();
  4149.     }
  4150.     }
  4151.  
  4152.     // enable/disable cmd key shortcuts and menu items
  4153.     sv->updateCommandAvailability();
  4154. }
  4155.  
  4156. ////////////////////////////////////////////////////////////////////////
  4157. //
  4158. //  Remove selected objects from the scene graph. 
  4159. //  In this demo, we don't really know how the graphs are set up,
  4160. //  so act conservatively, and simply remove the node which is the
  4161. //  tail of the path from its parent. Note if the node is instanced,
  4162. //  all instances will be destroyed. Then travel up the path to a
  4163. //  parent separator. If there are no other shapes under the separator,
  4164. //  destroy it too.
  4165. //
  4166. //  Other applications might delete selected objects a different way,
  4167. //  depending on how the data is organized in the scene graph.
  4168. //
  4169. //  Use: protected
  4170. //
  4171. void
  4172. SoSceneViewer::destroySelectedObjects()
  4173. //
  4174. ////////////////////////////////////////////////////////////////////////
  4175. {
  4176.     for (int i = selection->getNumSelected() - 1; i >= 0; i--) {
  4177.     SoPath *p = (*selection)[i];
  4178.     p->ref();
  4179.     
  4180.     // Deselect this path
  4181.     selection->deselect(i);
  4182.  
  4183.     // Remove the tail node from the graph
  4184.     SoGroup *g = (SoGroup *) p->getNodeFromTail(1);
  4185.     g->removeChild(p->getTail());
  4186.     
  4187.     // Travel up the path to separators, and see if this was
  4188.     // the only shape node under the sep. If so, delete the sep too.
  4189.     // (Don't go all the way up to the selection node).
  4190.     SbBool shapeFound = FALSE;
  4191.     int j = 0;
  4192.     while ((! shapeFound) && (j < p->getLength() - 1)) {
  4193.         SoNode *n = p->getNodeFromTail(j);
  4194.         if (n->isOfType(SoSeparator::getClassTypeId())) {
  4195.         // Search for other shape nodes
  4196.         SoSearchAction sa;
  4197.         sa.setFind(SoSearchAction::TYPE);
  4198.         sa.setType(SoShape::getClassTypeId());
  4199.         sa.apply(n);
  4200.         
  4201.         // If no other shapes under this separator, delete it!
  4202.         if (sa.getPath() == NULL) {
  4203.             g = (SoGroup *) p->getNodeFromTail(j + 1);
  4204.             g->removeChild(n);
  4205.             
  4206.             // Reset j since we have a new end of path
  4207.             j = 0;
  4208.         }
  4209.         else shapeFound = TRUE;
  4210.         }
  4211.         // Else a group with no children?
  4212.         else if (n->isOfType(SoGroup::getClassTypeId()) &&
  4213.         (((SoGroup *)n)->getNumChildren() == 0)) {
  4214.             g = (SoGroup *) p->getNodeFromTail(j + 1);
  4215.             g->removeChild(n);
  4216.             
  4217.             // Reset j since we have a new end of path
  4218.             j = 0;
  4219.         }
  4220.         // Else continue up the path looking for separators
  4221.         else j++;
  4222.     }
  4223.     
  4224.     p->unref();
  4225.     }
  4226. }
  4227.  
  4228. ////////////////////////////////////////////////////////////////////////
  4229. //
  4230. //  This enables/disables cmd key shortcuts and menu items
  4231. //  based on whether there are any objects, and/or any selected objects
  4232. //  in the scene graph.
  4233. //
  4234. //  Use: static private
  4235. //
  4236. // 
  4237. void
  4238. SoSceneViewer::updateCommandAvailability()
  4239. //
  4240. ////////////////////////////////////////////////////////////////////////
  4241. {
  4242.     Arg args[1];
  4243.  
  4244.     // enable/disable based on the number of child objects in scene
  4245.     if (selection->getNumChildren() == 0)
  4246.      XtSetArg(args[0], XmNsensitive, False);
  4247.     else XtSetArg(args[0], XmNsensitive, True);
  4248.     
  4249.     // save (if no children, nothing to save)
  4250.     XtSetValues(menuItems[SV_FILE_SAVE].widget, args, 1);
  4251.     XtSetValues(menuItems[SV_FILE_SAVE_AS].widget, args, 1);
  4252.     
  4253.     // pickAll (if no children, nothing to pick)
  4254.     XtSetValues(menuItems[SV_EDIT_PICK_ALL].widget, args, 1);
  4255.  
  4256.  
  4257.     // enable/disable based on the number of selected objects
  4258.     if (selection->getNumSelected() == 0)
  4259.      XtSetArg(args[0], XmNsensitive, False);
  4260.     else XtSetArg(args[0], XmNsensitive, True);
  4261.     
  4262.     // if nothing selected, then cannot pick parent, cut, copy, delete,
  4263.     // view selection, bring up editors
  4264.     XtSetValues(menuItems[SV_EDIT_PICK_PARENT].widget, args, 1);
  4265.     XtSetValues(menuItems[SV_EDIT_CUT].widget, args, 1);
  4266.     XtSetValues(menuItems[SV_EDIT_COPY].widget, args, 1);
  4267.     XtSetValues(menuItems[SV_EDIT_DELETE].widget, args, 1);
  4268.     XtSetValues(menuItems[SV_VIEW_SELECTION].widget, args, 1);
  4269.     XtSetValues(menuItems[SV_EDITOR_TRANSFORM].widget, args, 1);
  4270.     XtSetValues(menuItems[SV_EDITOR_MATERIAL].widget, args, 1);
  4271.     XtSetValues(menuItems[SV_EDITOR_COLOR].widget, args, 1);
  4272. }
  4273.  
  4274. ////////////////////////////////////////////////////////////////////////
  4275. //
  4276. //  Called by Xt when a menu is about to be displayed.
  4277. //  This gives us a chance to update any items in the menu.
  4278. //
  4279. //  Use: static private
  4280. //
  4281. void
  4282. SoSceneViewer::menuDisplay(Widget, SoSceneViewerData *data, XtPointer)
  4283. //
  4284. ////////////////////////////////////////////////////////////////////////
  4285. {
  4286.     SoSceneViewer *sv = data->classPt;
  4287.     Arg args[1];
  4288.     char str[100];
  4289.     XmString xmstr;
  4290.     
  4291.     switch (data->id) {
  4292.     case SV_FILE:
  4293.         // disable saving if there isn't any geometry
  4294.         if (sv->selection->getNumChildren() == 0)
  4295.             XtSetArg(args[0], XmNsensitive, False);
  4296.         else 
  4297.             XtSetArg(args[0], XmNsensitive, True);
  4298.         
  4299.         XtSetValues(sv->menuItems[SV_FILE_SAVE].widget, args, 1);
  4300.         XtSetValues(sv->menuItems[SV_FILE_SAVE_AS].widget, args, 1);
  4301.         
  4302.         // update the "Save" menu entry to reflect the current file name
  4303.         strcpy(str, "Save");
  4304.         if (sv->fileName != NULL) {
  4305.         // get the file name withought the entire path
  4306.         char *pt = strrchr(sv->fileName, '/');   // last occurance of '/'
  4307.         pt = (pt == NULL) ? sv->fileName : pt + 1;
  4308.         strcat(str, " -> ");
  4309.         strcat(str, pt);
  4310.         }
  4311.         xmstr = XmStringCreate(str, XmSTRING_DEFAULT_CHARSET);
  4312.         XtSetArg(args[0], XmNlabelString, xmstr);
  4313.         XtSetValues(sv->menuItems[SV_FILE_SAVE].widget, args, 1);
  4314.         XmStringFree(xmstr);
  4315.         break;
  4316.         
  4317. #ifndef EXPLORER
  4318.         case SV_EDIT:
  4319.         // disable cut, copy, delete, pickParent if there is no selection
  4320.         if(sv->selection->getNumSelected() == 0)
  4321.             XtSetArg(args[0], XmNsensitive, False);
  4322.         else 
  4323.             XtSetArg(args[0], XmNsensitive, True);
  4324.         
  4325.         XtSetValues(sv->menuItems[SV_EDIT_PICK_PARENT].widget, args, 1);
  4326.         XtSetValues(sv->menuItems[SV_EDIT_CUT].widget, args, 1);
  4327.         XtSetValues(sv->menuItems[SV_EDIT_COPY].widget, args, 1);
  4328.         XtSetValues(sv->menuItems[SV_EDIT_DELETE].widget, args, 1);
  4329.         
  4330.         // disable pick all if there are nothing to pick
  4331.         if (sv->selection->getNumChildren() == 0)
  4332.             XtSetArg(args[0], XmNsensitive, False);
  4333.         else 
  4334.             XtSetArg(args[0], XmNsensitive, True);
  4335.         XtSetValues(sv->menuItems[SV_EDIT_PICK_ALL].widget, args, 1);
  4336.         break;
  4337. #endif /* EXPLORER */
  4338.         
  4339.     case SV_VIEW:
  4340.         // set pick/edit toggle
  4341. #ifdef notdef
  4342.         if ( sv->isViewing() )
  4343.          TOGGLE_OFF(sv->menuItems[SV_VIEW_PICK].widget);
  4344.         else TOGGLE_ON(sv->menuItems[SV_VIEW_PICK].widget);
  4345.         
  4346. #ifdef EXPLORER
  4347.         // set user pick toggle
  4348.         if ( sv->userModeFlag )
  4349.          TOGGLE_ON(sv->menuItems[SV_VIEW_USER].widget);
  4350.         else TOGGLE_OFF(sv->menuItems[SV_VIEW_USER].widget);
  4351. #endif
  4352.         // Set the correct viewer
  4353.         TOGGLE_OFF(sv->menuItems[SV_VIEW_EXAMINER].widget);
  4354.         TOGGLE_OFF(sv->menuItems[SV_VIEW_WALK].widget);
  4355.         TOGGLE_OFF(sv->menuItems[SV_VIEW_PLANE].widget);
  4356.         TOGGLE_OFF(sv->menuItems[SV_VIEW_FLY].widget);
  4357.         switch ( sv->whichViewer ) {
  4358.         case SV_VWR_EXAMINER:
  4359.             TOGGLE_ON(sv->menuItems[SV_VIEW_EXAMINER].widget);
  4360.             break;
  4361.         case SV_VWR_WALK:
  4362.             TOGGLE_ON(sv->menuItems[SV_VIEW_WALK].widget);
  4363.             break;
  4364.         case SV_VWR_FLY:
  4365.             TOGGLE_ON(sv->menuItems[SV_VIEW_FLY].widget);
  4366.             break;
  4367.         case SV_VWR_PLANE:
  4368.             TOGGLE_ON(sv->menuItems[SV_VIEW_PLANE].widget);
  4369.             break;
  4370.         }
  4371. #endif
  4372.         
  4373.         // set the correct transparency type
  4374.         TOGGLE_OFF(sv->menuItems[SV_VIEW_SCREEN_TRANSPARENCY].widget);
  4375.         TOGGLE_OFF(sv->menuItems[SV_VIEW_BLEND_TRANSPARENCY].widget);
  4376.         TOGGLE_OFF(sv->menuItems[SV_VIEW_DELAY_BLEND_TRANSPARENCY].widget);
  4377.         TOGGLE_OFF(sv->menuItems[SV_VIEW_SORT_BLEND_TRANSPARENCY].widget);
  4378.         switch( sv->getTransparencyType() ) {
  4379.         case SoGLRenderAction::SCREEN_DOOR:
  4380.             TOGGLE_ON(sv->menuItems[SV_VIEW_SCREEN_TRANSPARENCY].widget);
  4381.             break;
  4382.         case SoGLRenderAction::BLEND:
  4383.             TOGGLE_ON(sv->menuItems[SV_VIEW_BLEND_TRANSPARENCY].widget);
  4384.             break;
  4385.         case SoGLRenderAction::DELAYED_BLEND:
  4386.             TOGGLE_ON(sv->menuItems[SV_VIEW_DELAY_BLEND_TRANSPARENCY].widget);
  4387.             break;
  4388.         case SoGLRenderAction::SORTED_OBJECT_BLEND:
  4389.             TOGGLE_ON(sv->menuItems[SV_VIEW_SORT_BLEND_TRANSPARENCY].widget);
  4390.             break;
  4391.         }
  4392.         
  4393.         // disable view selection if nothing is selected
  4394.         if ( sv->selection->getNumSelected() == 0 )
  4395.             XtSetArg(args[0], XmNsensitive, False);
  4396.         else 
  4397.             XtSetArg(args[0], XmNsensitive, True);
  4398.         XtSetValues(sv->menuItems[SV_VIEW_SELECTION].widget, args, 1);
  4399.         
  4400.         // set fog toggle
  4401.         if ( sv->fogFlag )
  4402.          TOGGLE_ON(sv->menuItems[SV_VIEW_FOG].widget);
  4403.         else TOGGLE_OFF(sv->menuItems[SV_VIEW_FOG].widget);
  4404.         
  4405.         // set antialiasing toggle
  4406.         if ( sv->antialiasingFlag )
  4407.          TOGGLE_ON(sv->menuItems[SV_VIEW_ANTIALIASING].widget);
  4408.         else TOGGLE_OFF(sv->menuItems[SV_VIEW_ANTIALIASING].widget);
  4409.         
  4410.         break;
  4411.         
  4412. #ifndef EXPLORER
  4413.         case SV_SELECTION:
  4414.         // mirror the selection policy
  4415.         TOGGLE_OFF(sv->menuItems[SV_SEL_SINGLE_SELECT].widget);
  4416.         TOGGLE_OFF(sv->menuItems[SV_SEL_TOGGLE_SELECT].widget);
  4417.         TOGGLE_OFF(sv->menuItems[SV_SEL_SHIFT_SELECT].widget);
  4418.         switch ( sv->selection->policy.getValue() ) {
  4419.         case SoSelection::SINGLE:
  4420.             TOGGLE_ON (sv->menuItems[SV_SEL_SINGLE_SELECT].widget);
  4421.             break;
  4422.             case SoSelection::TOGGLE:
  4423.             TOGGLE_ON (sv->menuItems[SV_SEL_TOGGLE_SELECT].widget);
  4424.             break;
  4425.             case SoSelection::SHIFT:
  4426.             TOGGLE_ON (sv->menuItems[SV_SEL_SHIFT_SELECT].widget);
  4427.             break;
  4428.         default:
  4429.             fprintf(stderr, "INTERNAL ERROR, unknown selection policy\n");
  4430.             break;
  4431.         }
  4432.         
  4433.         break;
  4434. #endif /* EXPLORER */
  4435.         
  4436.         case SV_EDITOR:
  4437.         // disable items if there is no selection
  4438.         if(sv->selection->getNumSelected() == 0)
  4439.             XtSetArg(args[0], XmNsensitive, False);
  4440.         else 
  4441.             XtSetArg(args[0], XmNsensitive, True);
  4442.         
  4443.         XtSetValues(sv->menuItems[SV_EDITOR_TRANSFORM].widget, args, 1);
  4444.         XtSetValues(sv->menuItems[SV_EDITOR_MATERIAL].widget, args, 1);
  4445.         XtSetValues(sv->menuItems[SV_EDITOR_COLOR].widget, args, 1);
  4446.         break;
  4447.         
  4448.        case SV_MANIP:
  4449.  
  4450.         // First, the section with the different types of manipulators.
  4451.         TOGGLE_OFF( sv->menuItems[SV_MANIP_HANDLEBOX].widget );
  4452.         TOGGLE_OFF( sv->menuItems[SV_MANIP_TRACKBALL].widget );
  4453.         TOGGLE_OFF( sv->menuItems[SV_MANIP_JACK].widget );
  4454.         TOGGLE_OFF( sv->menuItems[SV_MANIP_CENTERBALL].widget );
  4455.         TOGGLE_OFF( sv->menuItems[SV_MANIP_XFBOX].widget );
  4456.         TOGGLE_OFF( sv->menuItems[SV_MANIP_TABBOX].widget );
  4457.         TOGGLE_OFF( sv->menuItems[SV_MANIP_NONE].widget );
  4458.         
  4459.         // Turn appropriate radio button on
  4460.         if (sv->curManip == SV_HANDLEBOX)
  4461.         TOGGLE_ON(sv->menuItems[SV_MANIP_HANDLEBOX].widget);
  4462.         else if (sv->curManip == SV_TRACKBALL)
  4463.         TOGGLE_ON(sv->menuItems[SV_MANIP_TRACKBALL].widget);
  4464.         else if (sv->curManip == SV_JACK)
  4465.         TOGGLE_ON(sv->menuItems[SV_MANIP_JACK].widget);
  4466.         else if (sv->curManip == SV_CENTERBALL)
  4467.         TOGGLE_ON(sv->menuItems[SV_MANIP_CENTERBALL].widget);
  4468.         else if (sv->curManip == SV_XFBOX)
  4469.         TOGGLE_ON(sv->menuItems[SV_MANIP_XFBOX].widget);
  4470.         else if (sv->curManip == SV_TABBOX)
  4471.         TOGGLE_ON(sv->menuItems[SV_MANIP_TABBOX].widget);
  4472.         else 
  4473.         TOGGLE_ON(sv->menuItems[SV_MANIP_NONE].widget);
  4474.  
  4475.         // Next, the toggle that says whether we replace current 
  4476.         // manipulators every time we change the type given in the menu.
  4477.         if (sv->curManipReplaces == TRUE ) 
  4478.         TOGGLE_ON(  sv->menuItems[SV_MANIP_REPLACE_ALL].widget );
  4479.         else
  4480.         TOGGLE_OFF( sv->menuItems[SV_MANIP_REPLACE_ALL].widget );
  4481.  
  4482.         break;
  4483.         
  4484.     case SV_LIGHT:
  4485.         // disable the add light entries if we have more than 8 lights
  4486.         if (sv->lightDataList.getLength() < 8)
  4487.             XtSetArg(args[0], XmNsensitive, True);
  4488.         else 
  4489.             XtSetArg(args[0], XmNsensitive, False);
  4490.         
  4491.         XtSetValues(sv->menuItems[SV_LIGHT_ADD_DIRECT].widget, args, 1);
  4492.         XtSetValues(sv->menuItems[SV_LIGHT_ADD_POINT].widget, args, 1);
  4493.         XtSetValues(sv->menuItems[SV_LIGHT_ADD_SPOT].widget, args, 1);
  4494.         
  4495.         // update the headlight label (show on/off with '*')
  4496.         sv->isHeadlight() ? strcpy(str, "* ") : strcpy(str, "  ");
  4497.         strcat(str, sv->headlightData->name);
  4498.         xmstr = XmStringCreate(str, XmSTRING_DEFAULT_CHARSET);
  4499.         XtSetArg(args[0], XmNlabelString, xmstr);
  4500.         XtSetValues(sv->headlightData->cascadeWidget, args, 1);
  4501.         XmStringFree(xmstr);
  4502.         
  4503.         // update the lights label (show on/off with '*')
  4504.         {
  4505.         for (int i=0; i < sv->lightDataList.getLength(); i++) {
  4506.             SvLightData *data = (SvLightData *) sv->lightDataList[i];
  4507.             IS_LIGHT_ON(data->lightSwitch) ? strcpy(str, "* ") : strcpy(str, "  ");
  4508.             strcat(str, data->name);
  4509.             xmstr = XmStringCreate(str, XmSTRING_DEFAULT_CHARSET);
  4510.             XtSetArg(args[0], XmNlabelString, xmstr);
  4511.             XtSetValues(data->cascadeWidget, args, 1);
  4512.             XmStringFree(xmstr);
  4513.         }
  4514.         }
  4515.         break;
  4516.         
  4517.     default:
  4518.         break;
  4519.     }
  4520. }
  4521.  
  4522. ////////////////////////////////////////////////////////////////////////
  4523. //
  4524. // Description:
  4525. //    Determines whether a given node is affected by a transform.
  4526. //
  4527. // Use: static, public
  4528. //
  4529. SbBool
  4530. SoSceneViewer::isAffectedByTransform(
  4531.     SoNode *theNode )        // node to be affected?
  4532. //
  4533. ////////////////////////////////////////////////////////////////////////
  4534. {
  4535.     if ( theNode->isOfType( SoGroup::getClassTypeId() )
  4536.         || theNode->isOfType( SoShape::getClassTypeId() )
  4537.         || theNode->isOfType( SoCamera::getClassTypeId() )
  4538.         || theNode->isOfType( SoLight::getClassTypeId() ) )  {
  4539.     return TRUE;
  4540.     }
  4541.     return FALSE;
  4542. }
  4543.  
  4544. ////////////////////////////////////////////////////////////////////////
  4545. //
  4546. // Description:
  4547. //    Determines whether a given node is affected by material node.
  4548. //
  4549. // Use: static, public
  4550. //
  4551. SbBool
  4552. SoSceneViewer::isAffectedByMaterial(
  4553.     SoNode *theNode )        // node to be affected?
  4554. //
  4555. ////////////////////////////////////////////////////////////////////////
  4556. {
  4557.     if ( theNode->isOfType( SoGroup::getClassTypeId() )
  4558.         || theNode->isOfType( SoShape::getClassTypeId() ) ) {
  4559.     return TRUE;
  4560.     }
  4561.     return FALSE;
  4562. }
  4563.  
  4564. ////////////////////////////////////////////////////////////////////////
  4565. //
  4566. // Description:
  4567. //    Create the lights and camera environment structure.
  4568. //
  4569. // Use: private
  4570. //
  4571. void
  4572. SoSceneViewer::createLightsCameraEnvironment()
  4573. //
  4574. ////////////////////////////////////////////////////////////////////////
  4575. {
  4576.     // Group {
  4577.     //      Label { "SoSceneViewer Environment v3.0" }
  4578.     //    Camera {}
  4579.     //    Environment {}
  4580.     //    Group {
  4581.     //        Switch { Light 1 }        # switch is child 0, light is child 0
  4582.     //        Switch { Light 2 }        # switch is child 1, light is child 0
  4583.     //        ...
  4584.     //    }
  4585.     // }
  4586.     //
  4587.     // NOTE: since the camera may be switched by the viewer (ortho/perspective toggle)
  4588.     // make sure to get the camera from the viewer (and not cache the camera).
  4589.     
  4590.     lightsCameraEnvironment = new SoGroup;
  4591.     environment             = new SoEnvironment;
  4592.     lightGroup                 = new SoGroup;
  4593.     envLabel              = new SoLabel;
  4594.  
  4595.     envLabel->label.setValue(SV_ENV_LABEL);
  4596.     lightsCameraEnvironment->addChild(envLabel);
  4597. #ifndef EXPLORER
  4598.     // Explorer SceneViewer doesn't remove cameras from the Scene, so
  4599.     // don't add any...
  4600.     lightsCameraEnvironment->addChild(new SoPerspectiveCamera);
  4601. #endif
  4602.     lightsCameraEnvironment->addChild(environment);
  4603.     lightsCameraEnvironment->addChild(lightGroup);
  4604. }
  4605.  
  4606. ////////////////////////////////////////////////////////////////////////
  4607. //
  4608. // Description:
  4609. //    Brings up the "About..." dialog (same code as gavin demo programs)
  4610. //
  4611. // Use: private
  4612. //
  4613. void
  4614. SoSceneViewer::showAboutDialog()
  4615. //
  4616. ////////////////////////////////////////////////////////////////////////
  4617. {
  4618.     if (access("/usr/demos/Inventor/SceneViewer.about", R_OK) != 0)
  4619.     {
  4620.     system("xconfirm -t 'Sorry, could not find "
  4621.            "/usr/demos/Inventor/SceneViewer.about' > /dev/null");
  4622.     return;
  4623.     }
  4624.  
  4625.     char command[100];
  4626.     sprintf(command, "showcase -v /usr/demos/Inventor/SceneViewer.about");
  4627.  
  4628.     int err = system(command);
  4629.     if (err)
  4630.     {
  4631.     system("xconfirm -t 'You must install showcase"
  4632.            " for this function to work' > /dev/null");
  4633.     return;
  4634.     }
  4635. }
  4636.  
  4637. //
  4638. // define those generic virtual functions
  4639. //
  4640. const char *
  4641. SoSceneViewer::getDefaultWidgetName() const
  4642. { return "SoSceneViewer"; }
  4643.  
  4644. const char *
  4645. SoSceneViewer::getDefaultTitle() const
  4646. { return "SceneViewer"; }
  4647.  
  4648. const char *
  4649. SoSceneViewer::getDefaultIconTitle() const
  4650. { return "SceneViewer"; }
  4651.  
  4652.